Thread的join()方法理解

Thread的join()方法的官方文档给出的解释是:等待该线程终止。我们可以参考一下代码

public class Student1 extends Thread {	
	@Override 
	public void run(){		
		for(int i=1;i<10;i++){
			System.out.println(getName()+"-->"+i);
		}
	}
}

public class ReadMain {
	public static void main(String[] args) throws InterruptedException{
		Student1 s1 = new Student1();
		s1.setName("线程1"); //为线程命名		
		s1.start();
		//s1.join();
		for(int i=0;i<10;i++){
			System.out.println("main-->"+i);
		}
	}
}
我们在没有join()方法之前,执行结果:

main-->0
线程1-->1
main-->1
线程1-->2
main-->2
线程1-->3
main-->3
线程1-->4
线程1-->5
main-->4
线程1-->6
main-->5
线程1-->7
main-->6
线程1-->8
main-->7
线程1-->9
main-->8
main-->9
加上s1.join()方法后,结果如下:

线程1-->1
线程1-->2
线程1-->3
线程1-->4
线程1-->5
线程1-->6
线程1-->7
线程1-->8
线程1-->9
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
可以看出线程1先执行完后,再是主线程main执行,join()方法的官方解释是:等待该线程终止。根据上面的结果 我们可以推测是让调用s1.join()的线程(这里既是主线程main)等待被调用线程(这里既是线程1)执行完后再执行调用s1.join()的 线程(main线程),即将调用线程(main线程)阻塞。

这里只有两个线程,我们现在在添加两个线程线程2和线程3,观察输出结果:

线程1-->1
线程3--->0
线程2:-->1
线程3--->1
线程1-->2
线程3--->2
线程2:-->2
线程3--->3
线程1-->3
线程3--->4
线程2:-->3
线程3--->5
线程1-->4
线程3--->6
线程2:-->4
线程3--->7
线程1-->5
线程3--->8
线程2:-->5
线程3--->9
线程1-->6
线程1-->7
线程1-->8
线程1-->9
线程2:-->6
线程2:-->7
main-->0
线程2:-->8
main-->1
线程2:-->9
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
我们根据上面的结果可以得出其执行的顺序:

主线程main先执行到s1.start()之前,此时虽然有创建的线程,但没有启动,所以参与到抢夺cpu的只有主线程一个,所以为单线程,当主线程main执行到s1.start(),启动s1线程(此时抢夺cpu的线程有两个为:s1线程和主线程main),此时cpu可能执行s1线程也可能执行主线程main,当主线程抢到cpu,主线程执行到s3.start(),此时有三个线程参与到抢夺cpu的行动中(s1线程,s3线程,主线程main),他们谁抢到cpu就执行谁的线程中的指令,当主线程抢到cpu,主线程main执行s2.start()时,此时有四个线程参与到抢夺cpu的行动中(s1线程,s3线程,主线程main,s2线程),谁抢到cpu就执行谁的指令,当主线程main抢到cpu后,执行到s1.join(),然后就告诉调用s1.join()的线程(这里为主线程)你要阻塞,直到s1线程执行完后,主线程才可以执行(这里可以理解为主线程才就可以参与到抢夺cpu资源中去了,此时实际有资格抢夺cpu的只有三个线程:s1线程,s2线程,s3线程,这三个线程谁抢到cpu就可以执行谁的指令,此时主线程main处于阻塞状态,不参与抢夺cpu的行动),当s1线程执行完后,s2线程,s3线程和主线程三个中没有执行完的(此时s2线程和s3线程可能执行完,也可能没有执行完,主线程肯定没有执行完 )有参与到抢夺cpu的行动中,谁抢到就执行谁的,直到主线程执行完(此时所有的线程也就执行完了)。我们也可以从了另一个例子验证我们上面的解释。我们只是修改了Student1和ReadMain的代码

public class Student1 extends Thread {		
	@Override 
	public void run(){		
		for(int i=1;i<10;i++){
			System.out.println(getName()+"-->"+i);
			if(i==5){
				Student2 s2 = new Student2();
				s2.setName("线程2");
				s2.start();
				try {
					s2.join();
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
		}
	}
}

public class ReadMain {
	public static void main(String[] args) throws InterruptedException{
		Student1 s1 = new Student1();
		s1.setName("线程1"); //为线程命名		
		s1.start();
		for(int i=0;i<10;i++){
			System.out.println("main-->"+i);
		}
	}
}

main-->0
线程1-->1
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
线程1-->2
main-->7
线程1-->3
main-->8
线程1-->4
main-->9
线程1-->5
线程2:-->1
线程2:-->2
线程2:-->3
线程2:-->4
线程2:-->5
线程2:-->6
线程2:-->7
线程2:-->8
线程2:-->9
线程1-->6
线程1-->7
线程1-->8
线程1-->9

从上面的输出可以解释执行的顺序:

前面的都一样,当我们执行到s1.start()时,此时有两个线程抢夺cpu(主线程 main和s1线程),当s1抢到cpu,执行其中的for循环,当执行到i==5时,进去if,创建了一个线程s2,并启动了线程2,然后,有三个线程参与抢夺cpu(主线程main,s1线程,s2线程),若s1抢到cpu,则执行到s2.join(),此时调用 s2.join()的线程(s1线程)阻塞,此时有两个线程(s2线程,主线程main)参与到抢夺cpu行动中(s1此时正在被阻塞,无法参与抢夺cpu,只有等到s2线程执行完后,s1线程才可以参与到抢夺cpu中),所以当s2线程执行完后,s1线程才可以和主线程main参与到抢夺cpu行动中(若没有执行完则参与到其中)。

join()这里不带参数意思为一直等下去,无限等待,若加上参数则为调用线程等待的最大时间数,若在此时间内被调用的线程没有执行完,则意味着调用线程可以重新参与到竞争cpu中去。






展开阅读全文

没有更多推荐了,返回首页