在某种时候,我们需要解析一个主任务里的多份子数据时,可以考虑使用多线程,每个线程负责一个子任务,在所有子任务完成后,再回到主任务,在这个过程中,如果要实现使主线程等待所有线程结束后再进行解析的操作,最简单的办法是使用join方法。线程在使用join方法后,会让当前执行线程等待join线程执行结束。其实现原理是不停检查join线程是否存活,如果join线程存活则让当前线程永远保持wait(0),即永远等待下去。在join线程中止后,当前线程会调用notifyAll()方法唤醒在此对象监视器上等待的所有线程进行竞争。
在JDK1.5之后,又出现了一个新的类,能够实现与join方法相同的功能,并更为完善。
Countdownlatch(闭锁)是一个在JDK1.5之后被引入的同步工具类,它允许一个或多个线程一直等待,直到其它线程的操作执行完后再执行,跟他一起被引入的还有CyclicBarrier、Semaphore、ConcurrentHashMap以及BlockingQueue,同属于java.util.concurrent包。在Java并发中,Countdownlatch的概念,极为重要。
Countdownlatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
Countdownlatch是通过一个计数器来实现的,计数器的初始值为线程的数量,每当一个线程完成了自己的任务之后,计数器的值就会减1。而当计数器的值达到0时,他表示所有的线程已经完成了任务,此时闭锁上等待的线程,就可以恢复执行任务。计数值只能被设置一次(Countdownlatch未提供任何机制对该值进行设置)。
Countdownlatch的工作机制为:
Main thread start
Create CountDownLatch for N threads
Create and start N threads
Main thread wait on latch
N threads completes there tasks are returns
Main thread resume execution
主线程必须在启动其他线程后立即调用 CountDownLatch.await()方法,这是主线程与 Countdownlatch的第一次交互,此时主线程阻塞,直到其他线程完成各自的任务。由主线程创建的其他N个线程必须引用闭锁对象,去通知CountDownLatch对象,他们已经完成了各自的任务,而每当一个线程在完成各自任务时调用一次这个方法,在构造函数中初始化的count值就减1。当N个线程都调用了这个方法后,count的值等于0,此时的主线程就能通过await()方法,恢复执行自己的任务。由主线程创建的N个子线程通过 CountDownLatch.countDown()方法通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。
其他N 个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务。这种通知机制是通过 CountDownLatch.countDown()方法来完成的;每调用一次这个方法,在构造函数中初始化的count值就减1。所以当N个线程都调 用了这个方法,count的值等于0,然后主线程就能通过await()方法,恢复执行自己的任务。
Countdownlatch类通常用于实现最大的并行性,或在主线程在开始执行前,需要等待N个线程完成各自的任务才能进行工作。在某些时候Countdownlatch类还可以用于检测程序是否会发生死锁,即对一个共享资源,使用多个线程进行访问,每次测试不同的数目而达到测试死锁的目的。
Countdownlatch类中有几个重要的方法:
| void await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被 中断 。 |
使当前线程在锁存器倒计数至零之前一直等待,除非线程被 中断 或超出了指定的等待时间。 | |
递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 | |
long getCount() 返回当前计数。 | |
| String toString() 返回标识此锁存器及其状态的字符串。 |