(See http://www.suneca.com/article.asp?id=54)
Thread API 包含了等待另一个线程完成的方法:join() 方法。当调用 Thread.join() 时,调用线程将阻塞,直到被join方法加入的目标线程完成为止。 可以于解起来抽象一睦,现在我们来举一个例子说明问题。
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public void run() {
//循环输出1 到 100
for(int i=1;i<=10;i++){
System.out.println("子线程输出:" + i);
}
}
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread();
//线程进入运行态
t1.start();
//主线程输出
System.out.println("主线程输出:http://www.suneca.com");
}
}
输出结果为:
从结果我们可以看到,对于我们运行MyThread这个程序,在主线程当中创建一个新的子线程,新的子线程启动后,主线程输出博客地址。因为当前主线程处于运行态,而子线程是处于可运行态,所以输出的结果为先输出网址,再输出了线程输出的数据。但也有可能会出现这种情况,子线程进入可运行态之后,马上进入运行态,那此时的是输出子线程的数据,再输出主线程的数据,它们的执行完全是由线程调度器进行调度。我们再将程序做如下的修改:
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public void run() {
//循环输出1 到 100
for(int i=1;i<=100;i++){
System.out.println("子线程输出:" + i);
}
}
/**
* 应用程序入口.
*
* @param args
*/
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread();
//线程进入运行态
t1.start();
//主线程输出
for(int i=0;i<100;i++){
System.out.println("主线程输出:http://www.suneca.com");
}
}
}
此时的输出结果为:
主线程跟子线程在线程调度器的调度下,相互抢夺CPU资源,交叉运行着!
那么,如果我希望程序启用子线程,必须等待着子线程执行完毕之后,主线程才能继续执行下去,那该怎么办?那此时,我们就必须使用到join这个方法啦!当使用Thread.join()时,调用线程将阻塞。因为子线程是由主线程进行调用的,所以当子线程调用到join这个函数时,主线程将阻塞,必须等待子线程执行完毕之后,才能继续执行。使用join的程序:
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public void run() {
//循环输出1 到 100
for(int i=1;i<=10;i++){
System.out.println("子线程输出:" + i);
}
}
/**
* 应用程序入口.
*
* @param args
*/
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread();
//线程进入运行态
t1.start();
try {
//线程的调用者,即主线程阻塞!
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程输出
for(int i=0;i<10;i++){
System.out.println("主线程输出:http://www.suneca.com");
}
}
}
输出结果为:
从结果当中,我们可以看出,当子线程调用到join的时候,主线程将阻塞,子线程执行完毕之后,主线程才能继续执行。对于join()方法,还有两个重载方法,比如joing(long millis),那就是让主程序阻塞多长时间后才能恢复到可运行状态。
如果主程序调用了两个子线程,那这两个子线程是如何工作的呢?
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
public void run() {
//循环输出1 到 100
for(int i=1;i<=100;i++){
System.out.println("子线程输出:" + this.getName() + " - " + i);
}
}
/**
* 应用程序入口.
*
* @param args
*/
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread("t1");
MyThread t2 = new MyThread("t2");
//线程进入运行态
t1.start();
t2.start();
try {
//线程的调用者,即主线程阻塞!
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程输出
for(int i=0;i<10;i++){
System.out.println("主线程输出:http://www.suneca.com");
}
}
}
输出结果:
从图的输出结果我们可以看出:对于在线程t1跟线程t2当中都调用了线程的join()方法,那么,调用t1、t2的主线程将阻塞,等到t1、t2都执行完毕之后,再执行主线程。对于子线程t1跟t2,它们的一个先后顺序,如何进行调度,完全是由线程调度器进行管理!
五、多线程
从前面的例子我们已经可以看出,对于有两个以上的线程同时启动,那这些线程的执行的先后顺序我们是没有办法预知的,因为对于它们的调用完全是由线程调度器进行调用!当子线程t1跟t2同时处于运行状态,那谁先执行谁慢执行,由线程调度器决定。那我们程序能否进行控制呢?答案是可以的,我们可以通过设计优先级别来进行控制。每一个线程,默认的优先级别为5,值越高,表示优先级别越高(最高为10),值越低(最低为1),表示优先级别越低!当线先级别相等的时候,那就只有排队等待着线程调度器的调用。
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
public void run() {
//循环输出1 到 100
for(int i=1;i<=10;i++){
System.out.println("子线程输出:" + this.getName() + " - " + i);
}
}
/**
* 应用程序入口.
*
* @param args
*/
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread("t1");
MyThread t2 = new MyThread("t2");
//线程进入运行态
t1.start();
t2.start();
//主线程输出
System.out.println("主线程输出:http://www.suneca.com");
}
}
t1比t2先启动,进入可运行态,排队等候线程的调度。执行结果:
如果设置了线程的优先级,如:
/**
* 使用继承方式的线程实现.
*
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
public void run() {
//循环输出1 到 100
for(int i=1;i<=10;i++){
System.out.println("子线程输出:" + this.getName() + " - " + i);
}
}
/**
* 应用程序入口.
*
* @param args
*/
public static void main(String[] args){
//创建一个线程实例
MyThread t1 = new MyThread("t1");
MyThread t2 = new MyThread("t2");
//设置t1的的优先级别为最低
t1.setPriority(Thread.MIN_PRIORITY);
//设置t2的优先级别为最高
t2.setPriority(Thread.MAX_PRIORITY);
//线程进入运行态
t1.start();
t2.start();
//主线程输出
System.out.println("主线程输出:http://www.suneca.com");
}
}
执行结果:
从结果当中我们可以看到,最早的时候是由主线程执行,主线程占用着CPU资源,接着,线程t1启动,线程t1进入排队,等候着线程调度器的调度;再接着,线程t2启动,线程t2也进入排队。
我们从结果当中更可以发现一点,对于高于5的线程优先级别,它更容易从当中正在执行的线程(主线程)当中抢夺CPU资源。
多线程的另外一种情况就是,多个线程共享同一个线程实例。
/**
* 使用实现Runnable接口的线程.
*
* @author <a href='http://www.suneca.com'>ZIZZ</a>
*
* @Create-Time:2008 下午10:48:31
*/
public class MyRunnable implements Runnable{
public void run() {
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}
public static void main(String[] args){
//构造一个Runnable的实例
MyRunnable runnable = new MyRunnable();
//创建新的线程
Thread t1 = new Thread(runnable,"T1");
Thread t2 = new Thread(runnable,"T2");
//线程启动.
t1.start();
t2.start();
}
}
执行结果片断:
当多线程共享同一个线程实例的时候,我们需要考虑一下线程的同步问题。