在进入java平台的线程对象之前
基于基础篇(一)的一些问题
我先插入两个基本概念
线程的并发与并行
在单CPU系统中系统调度在某一时刻只能让一个线程运行虽然这种调试机制有多种形式(大多数是时间片轮巡为主)但无论如何要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent)而在多CPU系统中可以让两个以上的线程同时运行这种可以同时让两个以上线程同时运行的方式叫做并行(parallel)
在上面包括以后的所有论述中请各位朋友谅解我无法用最准确的词语来定义储如并发和并行这类术语但我以我的经验能通俗地告诉大家它是怎么一回事如果您看到我说的一些标准文档上说的不一样只要意思一致那您就不要挑刺了
Java线程对象
现在我们来开始考察JAVA中线程对象
在JAVA中要开始一个线程有两种方式一是直接调用Thread实例的start()方法二是
将Runable实例传给一个Thread实例然后调用它的start()方法
在前面已经说过线程对象和线程是两个完全不同的概念这里我们再次深入一下生成一个线程的实例并不代表启动了线程而启动线程是说在某个线程对象上启动了该实例对应的线程当该线程结束后并不会就立即消失
对于从很多书籍上可以看到的基础知识我就不用多说了既然是基础知识我也着重于从普通文档上读不到的内容所以本节我重点要说的是两种线程对象产生线程方式的区别
class MyThread extends Thread{
public int x = ;
public void run(){
for(int i=;i<;i++){
try{
Threadsleep()
}catch(Exception e){}
Systemoutprintln(x++)
}
}
}
如果我们生成MyThread的一个实例然后调用它的start()方法那么就产生了这个实例对应的线程
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread()
mtstart()
}
}
不用说最终会打印出到现在我们稍微玩一点花样
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread()
mtstart()
Systemoutprintln()
}
}
也不用说在基础篇(一)中我们知道由于单CPU的原因一般会先打印然后打印到不过我们可以控制线程让它按我们的意思来运行
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread()
mtstart()
mtjoin()
Systemoutprintln()
}
}
好了我们终于看到mt实例对应的线程(假如我有时说mt线程请你不要怪我不过我尽量不这么说)在运行完成后主线程才打印因为我们让当前线程(这里是主线程)等待mt线程的运行结束在线程对象a上调用join()方法就是让当前正在执行的线程等待线程对象a对应的线程运行完成后才继续运行 请大家一定要深刻理解并熟记这句话而我这里引出这个知识点的目的是为了让你继续看下面的例子
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread()
mtstart()
mtjoin()
Threadsleep()
mtstart()
}
}
当线程对象mt运行完成后我们让主线程休息一下然后我们再次在这个线程对象上启动线程结果我们看到
Exception in thread main javalangIllegalThreadStateException
也就是这种线程对象一时运行一次完成后它就再也不能运行第二次了我们可以看一下它有具体实现
public synchronized void start() {
if (started)
throw new IllegalThreadStateException()
started = true;
groupadd(this)
start()
}
一个Thread的实例一旦调用start()方法这个实例的started标记就标记为true事实中不管这个线程后来有没有执行到底只要调用了一次start()就再也没有机会运行了这意味着
通过Thread实例的start()一个Thread的实例只能产生一个线程
那么如果要在一个实例上产生多个线程(也就是我们常说的线程池)我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能
class R implements Runnable{
private int x = ;
public void run(){
for(int i=;i<;i++){
try{
Threadsleep()
}catch(Exception e){}
Systemoutprintln(x++)
}
}
}
正如它的名字一样Runnable的实例是可运行的但它自己并不能直接运行它需要被Thread对象来包装才行运行
public class Test {
public static void main(String[] args) throws Exception{
new Thread(new R())start()
}
}
当然这个结果和mtstart()没有什么区别但如果我们把一个Runnable实例给Thread对象多次包装我们就可以看到它们实际是在同一实例上启动线程
public class Test {
public static void main(String[] args) throws Exception{
R r = new R()
for(int i=;i<;i++)
new Thread(r)start()
}
}
x是实例对象但结果是x被加到了说明这个线程是在同一个r对象上运行的请大家注意因为这个例子是在单CPU上运行的所以没有对多个线程同时操作共同的对象进行同步这里是为了说明的方便而简化了同步而真正的环境中你无法预知程序会在什么环境下运行所以一定要考虑同步
到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别
package debug;
import javaio*;
import javalangThread;
class MyThread extends Thread{
public int x = ;
public void run(){
Systemoutprintln(++x)
}
}
class R implements Runnable{
private int x = ;
public void run(){
Systemoutprintln(++x)
}
}
public class Test {
public static void main(String[] args) throws Exception{
for(int i=;i<;i++){
Thread t = new MyThread()
tstart()
}
Threadsleep()//让上面的线程运行完成
R r = new R()
for(int i=;i<;i++){
Thread t = new Thread(r)
tstart()
}
}
}
上面个线程对象产生的个线程运行时打印了次下面个线程对象产生的个线程运行时打印了到我们把下面的个线程称为同一实例(Runnable实例)的多个线程