假如有这样一个场景,程序员小余今天的任务是:实现 3 个需求,修改 3 个 bug,完成这些之后才能下班。可以使用两种方法实现。
1. 使用 join 方法
代码如下:
public class JoinTest {public static void main(String[] args) throws InterruptedException{Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {for(int i =1;i<4;i++) {System.out.println( "已实现了第"+i+"需求");
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {for(int i = 1;i<4;i++) {System.out.println("已修改完第"+i+"个 Bug");
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("任务完成,可以下班了");
}
}
执行了很多次,结果都符合要求,随机选择一次结果如下:
可以看到,程序员小张是实现完需求并且改完 bug 才下班的。如果不调用线程 1 和线程 2 的 join 方法话,就不能保证是完成任务后才下班的。查看 join 方法的实现代码可以看到如下代码:
while (isAlive()) {wait(0);
}
其原理就是不停检查 join 线程是否存活,如果 join 线程存活则让当前线程永远等待。直到线程中止后,线程的 this.notifyAll() 方法才会调用,调用 notifyAll() 方法是在 JVM 里实现的,所以 JDK 里看不到。
2. 使用 CountDownLatch
在 JDK1.5 之后的并发包中提供了 CountDownLatch 也可以实现 join 的功能,它允许一个或多个线程等待其他线程完成操作。
常用 API 如下:
CountDownLatch(int count) // 实例化一个倒计数器,count 指定计数个数
countDown() // 计数减一
await() // 等待,当计数减到 0 时,所有线程并行执行
下面使用 CountDownLatch 来实现之前的代码:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {static final CountDownLatch c = new CountDownLatch(6);// 构造一个大小的为 6 的计数器
public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {for(int i =1;i<4;i++) {System.out.println( "已实现了第"+i+"需求");
c.countDown();// 执行一次该线程,计数器减 1}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {for(int i = 1;i<4;i++) {System.out.println("已修改完第"+i+"个 Bug");
c.countDown();// 执行一次该线程,计数器减 1}
}
});
thread1.start();
thread2.start();
c.await();// 在此处等待,当计数器减到 0 时,才不会阻塞当前线程,继续往下执行
System.out.println("任务完成,可以下班了");
}
}
CountDownLatch 的构造参数接受一个 int 类型的参数作为计数器,如果想 N 个线程完成,就传入 N。当调用 countDown 方法时,N 就会减 1,await 方法会阻塞当前线程,直到 N 变为 0.