Latch 的相关笔记
背景介绍 :
在java多线程中 , 当用到相关的Worker Thread模式的时候,启动多的Worker 线程执行方法doWork(). 伪代码如下:
for (int i = 0; i < N; ++i)
new Thread(new Worker()).start();
doSomething();
这个时候,这些后台线程将会立刻与soSomething()方法并行执行执行相关的run()方法. 现在需要一个解决方案,目的是让这些工作者线程在执行了doSomething()以后才开始执行相关的线程run()方法. 解决的方案有多种,仁者见仁,下面介绍的是用Latch的方式来完成线程的调度.
准备环境 :
下载EDU.oswego.cs.dl.util.concurrent相关的jar包以及源代码;
前提介绍 :
使用Latch的时候,可以先看一下相关的源代码. Latch的源代码并不多,实现了Sync接口,方法有三个,
并且是线程安全的. 下面逐一介绍 :
(1)acquire()方法: 此方法中,如果latched_变量(Latch的实例变量,boolean)为false, 就wait();这里多说一句,也算给自己提个醒, wait()会释放掉this锁并进入this的的等待区,等待notify()或者notifyAll()使之"醒来",但是醒来以后要重新获取this锁,并不是马上就能够执行的.
(2)attempt(long msecs):这个方法很有趣,仔细看源代码,这个方法并不是先睡上个msecs长的时间,而是将会首先判断是否latched_变量是否为ture,是就return ture表示可以进入临界区.不是的话将会进入for(;;)无限循环, 在循环中,首先wait(msecs). 特别注意,wait(msecs)并不是就一定要等待msecs时间,JDK 对此方法作出的解释是 :
此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中,然后放弃此对象上的所有同步要求。出于线程调度目的,线程 T 被禁用,且处于休眠状态,直到发生以下四种情况之一:
- 其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。
- 其他某个线程调用此对象的 notifyAll 方法。
-
其他某个线程
中断
线程 T。 - 已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,该线程将一直等待,直到获得通知。
然后,从对象的等待集中删除线程 T,并重新进行线程调度。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被还原到以前的状态 - 这就是调用 wait 方法时的情况。然后,线程 T 从 wait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调用 wait 方法时的情况完全相同。
明白了这一点,也许就不难理解为什么wait(smsecs)时间后还要用 :waitTime = msecs - (System.currentTimeMillis() - start);这样的语句判断是否超时.
(3) release() :这个方法将标志位latched_设置为ture,并进行notify操作.
运行机理 :
想想一下,出事状况下(Fatch中的标志位_latched初始值为false)当多个线程调用acquire()的时候,因为标志位为false,那么将集体进入等待区. 当外界调用此Fatch对象的release()方法时候,由于notifyAll()并且将标志位设置为ture的原因,等待区中的线程集体排着队来“通过检查门口”并进行后续操作. 这样就能达到问题需要解决的目标;
下面是在Latch类中的实例代码,我将之粘贴出来以保证Blog的完整性 :
class Worker implements Runnable {
* private final Latch startSignal;
* Worker(Latch l) { startSignal = l; }
* public void run() {
* startSignal.acquire();
* doWork();
* }
* void doWork() { ... }
* }
*
* class Driver { // ...
* void main() {
* Latch go = new Latch();
* for (int i = 0; i < N; ++i) // make threads
* new Thread(new Worker(go)).start();
* doSomethingElse(); // don't let run yet
* go.release(); // let all threads proceed
* }
* }