CountDownLatch——闭锁的实现之一
CountDownLatch实际上是一种闭锁实现。闭锁:是一种同步工具类,可以延迟线程的进度直到其到达终止状态——《Java并发编程实战》。这个怎么解释呢?简单来说,就是有1个线程需要等待其余10个线程都执行完毕后再执行,这个时候就可以使用闭锁,也即CountDownLatch(当然闭锁的实现并不止这一种)。关于对闭锁的详细解释请参考《Java并发编程实战》P79。
CountDownLatch中有一个计数器,该计数器通过构造方法传递,表示需要完成的工作。有两个主要的方法:countDown——表示计数器减1,再完成一个工作时调用此方法。await——表示唤醒,等待线程在执行方法前调用此方法,当计数器未为0(即还有工作尚未完成)时,被阻塞,当所有工作都已完成,计数器被减至0,此时等待线程才被唤醒以继续执行。
我们通过代码来实际感受一下CountDownLatch类API的使用。
首先有一个TaskThread任务线程,表示做好准备工作的线程。
1 package countdownlatch; 2 3 import java.util.concurrent.CountDownLatch; 4 5 /** 6 * Created by yulinfeng on 12/14/16. 7 */ 8 public class TaskThread implements Runnable { 9 private final CountDownLatch latch; 10 11 public TaskThread(CountDownLatch latch){ 12 this.latch = latch; 13 } 14 15 @Override 16 public void run() { 17 try { 18 doWork(); 19 latch.countDown(); //线程执行完这部分工作后,CountDownLatch的计数减1。 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 private void doWork() throws InterruptedException{ 26 System.out.println(Thread.currentThread().getName()); 27 Thread.sleep(2000); //休眠2s,模拟这部分工作的完成 28 } 29 }
接着是等待线程,即当所有的TaskThread完成各自的工作之后再执行此线程。
1 package countdownlatch; 2 3 import java.util.concurrent.CountDownLatch; 4 5 /** 6 * Created by yulinfeng on 12/14/16. 7 */ 8 public class WaitThread implements Runnable{ 9 private final CountDownLatch latch; 10 11 public WaitThread(CountDownLatch latch){ 12 this.latch = latch; 13 } 14 15 @Override 16 public void run() { 17 try { 18 System.out.println("Wait for other threads to execute!"); //就算CPU在未完成所有TaskThread进入到次线程,该线程也会因为CountDownLatch计数器未减至0而阻塞。 19 latch.await(); //直到CountDownLatch的计数器减至0(即表示所有的工作也完成)才继续执行,否则阻塞。 20 System.out.println("Other threads have already completed!"); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 25 } 26 }
测试代码:
1 package countdownlatch; 2 3 import java.util.concurrent.CountDownLatch; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 7 /** 8 * Created by yulinfeng on 12/14/16. 9 */ 10 public class Test { 11 12 public static void main(String[] args){ 13 ExecutorService exec = Executors.newCachedThreadPool(); 14 CountDownLatch latch = new CountDownLatch(100); //所有线程必须共享一个CountDownLatch单例(这也是在另外两个线程中CountDownLatch定义为final不可变引用的原因),模拟有100个工作待完成。 15 16 exec.execute(new WaitThread(latch)); //等待线程 17 for (int i = 0; i < 100; i++){ 18 exec.execute(new TaskThread(latch)); //开启100个线程,模拟完成100个工作。 19 } 20 21 exec.shutdown(); 22 } 23 }
执行结果很好的诠释了CountDownLatch:
不积跬步,无以至千里;不积小流,无以成江海。