哎哟~ 不错哦~'s Blog

Happy coding

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 方法时的情况。然后,线程 Twait 方法的调用中返回。所以,从 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
 *   }
 * }