isAlive()和join()的使用
我们之前说到,最好让主线程最后结束,在前面的例子中,都是通过调用sleep()方法,让线程睡眠产生时差来实现的,经过足够长时间的延迟以确保所有子线程都先于主线程结束。然而,这不是一个令人满意的解决方法。它也带来了一个大问题:一个线程如何直到另一个线程已经结束?幸运的是,Thread类提供了回答此问题的方法。
有两个方法可以判断一个线程是否已经结束。第一,可以在线程中调用isAlive()。这种方法由Thread类定义,它的通常形式为:
public final native boolean isAlive();
如果所调用线程仍在运行,isAlive()返回true,否则返回false。但isAlive()很少用到,等待线程结束更常用到的方法是调用join(),方法为:
public final void join() throws InterruptedException
该方法能够等待所调用线程结束。join()的重载方法允许给等待指定线程结束定义一个最大时间。
下面看一个例子:
public class Test3 {
public static void main(String[] args) throws InterruptedException {
//同时运行了三个子线程
MyThread1 t1=new MyThread1("child Thread One");
MyThread1 t2=new MyThread1("child Thread Two");
MyThread1 t3=new MyThread1("child Thread Three");
//等待所调用线程结束
t1.th.join();
t2.th.join();
t3.th.join();
System.out.println(t1.th.isAlive());
System.out.println(t2.th.isAlive());
System.out.println(t3.th.isAlive());
System.out.println("child Thread end ...");
System.out.println("main Thread end ...");
}
}
class MyThread1 implements Runnable{
public Thread th;
public MyThread1(String threadName) {
th=new Thread(this, threadName);
System.out.println("开始调用线程名称为"+threadName+"的start()方法");
th.start();
}
@Override
public void run() {
try {
for(int i=1;i<=5;i++){
th.sleep(1000);//让子线程睡眠1秒
System.out.println("大家好,我是线程名称为"+th.getName()+"的:"+i);
}
//捕获sleep()方法可能抛出的异常
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
主线程启动了三个子线程,让三个子线程一同运行,进而调用各自的join()方法等待它们线程结束,然后才让主线程结束。
开始调用线程名称为child Thread One的start()方法
开始调用线程名称为child Thread Two的start()方法
开始调用线程名称为child Thread Three的start()方法
大家好,我是线程名称为child Thread One的:1
大家好,我是线程名称为child Thread Three的:1
大家好,我是线程名称为child Thread Two的:1
大家好,我是线程名称为child Thread Three的:2
大家好,我是线程名称为child Thread One的:2
大家好,我是线程名称为child Thread Two的:2
大家好,我是线程名称为child Thread Three的:3
大家好,我是线程名称为child Thread One的:3
大家好,我是线程名称为child Thread Two的:3
大家好,我是线程名称为child Thread Two的:4
大家好,我是线程名称为child Thread One的:4
大家好,我是线程名称为child Thread Three的:4
大家好,我是线程名称为child Thread One的:5
大家好,我是线程名称为child Thread Three的:5
大家好,我是线程名称为child Thread Two的:5
false
false
false
child Thread end ...
main Thread end ...
线程优先级的作用
在第一节中,我们提到了通过setPriority(int level);来设置线程优先级,那么线程的优先级在线程运行过程中有什么用呢。
线程优先级被线程调度用来判断何时每个线程允许运行。理论上,优先级高的线程比优先级低的线程获得更多的CPU时间。实际上,线程获得的CPU时间通常由包括优先级在内的多个因素决定(例如,一个实行多任务处理的操作系统如何更有效的利用CPU时间)。
一个优先级高的线程自然比优先级低的线程优先。举例来,当低优先级的线程正在运行,而一个高优先级的线程被恢复(例如从睡眠中或等待I/O中),它将抢占低优先级线程所使用的CPU。
设置线程的优先级,用setPriority(int level)方法,该方法属于Thread类,它的通常格式为:
public final void setPriority(int newPriority);
这里level指定了对所调用线程的新的优先级的设置。level的值必须在1-10之间,否则会抛出IllegalArgumentException异常。
下面看一个例子:
public class Test4 {
public static void main(String[] args) throws InterruptedException {
MyThread2 t1=new MyThread2(3);
MyThread2 t2=new MyThread2(7);
t1.start();
t2.start();
Thread mainThread=Thread.currentThread();
mainThread.sleep(1000);//主线程睡眠1秒
t1.stop();
t2.stop();
t1.th.join();
t2.th.join();
System.out.println("One:"+t1.number);
System.out.println("Two:"+t2.number);
}
}
class MyThread2 implements Runnable {
public Thread th = null;
private volatile static boolean FLAG = true;
public int number=0;
public MyThread2(int level) {
th=new Thread(this);
th.setPriority(level);//设置优先级
}
@Override
public void run() {
while(this.FLAG){
number++;
}
}
public void stop(){
this.FLAG = false;
}
public void start(){
th.start();
}
}
在主线程中创建两个子线程,并把它们的优先级设置为3和7。两个线程被允许启动1秒。每个线程执行一个循环,1秒后,主线程终止了两个子线程。
运行结果:
One:558355176
Two:197268901
可以看出线程确实上下转换,甚至不屈从于CPU,也不被输入输出阻塞,优先级高的线程大约获得了80%的CPU时间。
上述程序还有个值得注意的地方,注意FLAG前的关键字volatile。用在此处以确保FLAG的值在下面的循环中每次得到验证。
while(this.FLAG){
number++;
}
如果不用volatile,Java可以自由的优化循环;FLAG的值被存在CPU的一个寄存器中,每次重复不一定需要复检。volatile的运用阻止了该优化,告知Java FLAG可以被改变,以确保FLAG的值在循环中每次得到验证。