3.4join方法
子线程需要进行大量运算,主线程早于子线程退出。那么主线程无法获取子线程执行后的信息。
『解决方法』主线程中调用子线程的 join,使得在子线程运行时,主线程进入 waiting 状态。join 之后主线程原地等待子线程销毁。
MyThread.java
public class MyThread extends Thread{
@Override
public void run(){
try{
long time = (long) (Math.random() * 10000);
System.out.println("time:" + time);
Thread.sleep(time);
}catch(Exception e){
}
}
}
Run.java
public class Run {
public static void main(String[] args) {
Thread t = new MyThread();
t.start();
try {
t.join();//使得主线程被无限期阻塞,等待子线程销毁
} catch (Exception e){
}
System.out.println("can");
System.out.println("you");
}
}
ThreadA.java
public class ThreadA extends Thread{
@Override
public void run(){
try {
for(int i = 0; ; i++){
String str = new String("fine");
if (isInterrupted()){
throw new InterruptedException();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
ThreadB.java
public class ThreadB extends Thread{
ThreadA a;
@Override
public void run(){
try{
a = new ThreadA();
a.start();
a.join();
System.out.println("线程B继续执行");
}catch (InterruptedException e){
System.out.println("线程B被中断");
e.printStackTrace();
a.interrupt();
}
}
}
Run.java
public class Run {
public static void main(String[] args) {
ThreadB b = new ThreadB();
b.start();
b.interrupt();
}
}
默认情况下,interrupt() 方法不会引起线程异常,像线程A。而像线程 B 有调用了它的 join(),再调用 interrupt() 方法就会引起线程的中断异常。
运行结果
- join 内部使用 wait() 来实现,所以会释放锁,允许其他线程进入同步区域。
- sleep 不会释放锁。
- join 之后主线程原地等待子线程销毁。
- 主线程调用 wait 需要获取子线程 t 的对象锁,因为 t.join() 涉及到 wait() 和同步。
ThreadA.java
public class ThreadA extends Thread{
ThreadB b;
public ThreadA(ThreadB b){
this.b = b;
}
@Override
public void run(){
try {
synchronized (b){
b.start();
b.join(6000);//释放了当前线程对 b 的锁
while(true){
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
执行结果
B 开始
B 服务
B 结束
ThreadA.java
public class ThreadA extends Thread{
ThreadB b;
public ThreadA(ThreadB b){
this.b = b;
}
@Override
public void run(){
try {
synchronized (b){
System.out.println("A start");
Thread.sleep(5000);//不释放锁,主线程无法访问 b
//b.wait();//会释放锁
System.out.println("A end");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
ThreadB.java
public class ThreadB extends Thread{
@Override
synchronized public void run(){
try{
System.out.println("B start");
Thread.sleep(6000);
System.out.println("B end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
Run.java
public class Run {
public static void main(String[] args) {
ThreadB b = new ThreadB();
ThreadA a = new ThreadA(b);
try{
a.start();//一旦线程 a 进入 run,会占有 b 锁并且不释放。
b.start();
b.join(4000);//这一行比前面两个 start 都要早执行,主线程阻塞等待 b 线程 4000ms。
System.out.println("Main end");
}catch (InterruptedException e){
}
}
}
执行顺序
运行结果有三种可能
- b.join(4000) 先拿到锁并且很快释放。
- a 拿到锁,开始执行,不释放锁。持续 5000ms。
- b.join(4000) 比线程 b 先拿到锁,但很快又释放,但此时已经过了 4000ms ,于是主线程不再阻塞,继续执行打印 Main end。
- b.start() 拿到锁就开始执行,不释放锁。持续 6000ms。
A start
A end
Main end
B start
B end
- 和第一种情况一样,只是 ThreadB 在争夺锁的时候比 Main end 的打印快了一点。
- Main end 和『B 开始』异步打印,Main end 慢了一点。
A start
A end
B start
Main end
B end
- b.join(4000) 先拿到锁并且很快释放。
- a 拿到锁,开始执行,不释放锁。持续 5000ms。
- 线程 b 比 b.join(4000) 先拿到锁,不释放锁,持续 6000ms。然后输出『结束』
- b.join(4000) 再次拿到锁。时间已经超过 4000ms, 主线程不再等待,打印 Main end。
A start
A end
B start
B end
Main end