CountDownLatch

CountDownLatch是一种灵活的闭锁实现。它可以使一个或多个线程等待一组事件的发生。

闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件的数量。countDown()方法递减计数器,表示有一个事件已经发生,而await方法等待计数器达到零,表示所有需要等待的事件都已经发生。如果计数器的值非零,那么await会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。

 1 package com.citi.test.mutiplethread.demo0503;
 2 
 3 import java.util.Date;
 4 import java.util.concurrent.CountDownLatch;
 5 
 6 public class TestHarness {
 7     public static void main(String[] args) {
 8         int counts=5;
 9         final CountDownLatch startGate=new CountDownLatch(1);
10         final CountDownLatch endGate=new CountDownLatch(counts);
11         for(int i=0;i){
12             new Thread(new Runnable() {
13                 @Override
14                 public void run() {
15                     System.out.println(Thread.currentThread().getName()+" is starting");
16                     try {
17                         Thread.sleep(1000);
18                         startGate.await();
19                         Thread.sleep(3000);
20                     } catch (InterruptedException e) {
21                         // TODO Auto-generated catch block
22                         e.printStackTrace();
23                     }
24                     endGate.countDown();
25                 }
26             }).start();
27         }
28         long start=System.nanoTime();
29         System.out.println(Thread.currentThread().getName()+" is running:"+new Date());
30         startGate.countDown();
31         System.out.println(Thread.currentThread().getName()+new Date());
32         try {
33             endGate.await();
34         } catch (InterruptedException e) {
35             // TODO Auto-generated catch block
36             e.printStackTrace();
37         }
38         long end=System.nanoTime();
39         System.out.println(end-start);
40     }
41 }

原理:

内部是用AQS实现。其中的count就是AQS中的state,

countDownLatch 主要有几个重要方法。await(), await(long time, TimeUnit unit). countDown()

  1 /**
  2  * Constructs a {@code CountDownLatch} initialized with the given count.
  3  *
  4  * @param count the number of times {@link #countDown} must be invoked
  5  *        before threads can pass through {@link #await}
  6  * @throws IllegalArgumentException if {@code count} is negative
  7  */
  8 public CountDownLatch(int count) {
  9     if (count < 0) throw new IllegalArgumentException("count < 0");
 10     this.sync = new Sync(count);
 11 }
 12 /**
 13  * Synchronization control For CountDownLatch.
 14  * Uses AQS state to represent count.
 15  这里可以看到是用AQS 中的state来表示这个count
 16  */
 17 private static final class Sync extends AbstractQueuedSynchronizer {}
 18 
 19 /**
 20  * Causes the current thread to wait until the latch has counted down to
 21  * zero, unless the thread is {@
 22  使当前线程等待直到闭锁倒数至零,除非线程被中断
 23  * 

If the current count is zero then this method returns immediately. 24 *如果当前计数是零,则这个方法直接返回。 25 *

If the current count is greater than zero then the current 26 * thread becomes disabled for thread scheduling purposes and lies 27 * dormant until one of two things happen: 28 如果当前计数大于0,则当前线程为了线程调度目的变得不可用,并处于休眠状态。 29 *

    30 *
  • The count reaches zero due to invocations of the 31 * {@link #countDown} method; or 32 *
  • Some other thread {@linkplain Thread#interrupt interrupts} 33 * the current thread. 34 *
35 *如果因为调用coutDown方法使计数变为0,或者其他线程中断当前线程。 36 *

If the current thread: 37 *

    38 *
  • has its interrupted status set on entry to this method; or 39 *
  • is {@linkplain Thread#interrupt interrupted} while waiting, 40 *
41 * then {@link InterruptedException} is thrown and the current thread's 42 * interrupted status is cleared. 43 *如果当前线程在进入此方法时设置了中断状态,或者当等待的时候被中断,则抛出中断异常并且清空当前线程的中断状态。 44 * @throws InterruptedException if the current thread is interrupted 45 * while waiting 46 */ 47 public void await() throws InterruptedException { 48 sync.acquireSharedInterruptibly(1); 49 } 50 51 AbstractQueuedSynchronizer.class 52 /** 53 * Acquires in shared mode, aborting if interrupted. Implemented 54 * by first checking interrupt status, then invoking at least once 55 * {@link #tryAcquireShared}, returning on success. Otherwise the 56 * thread is queued, possibly repeatedly blocking and unblocking, 57 * invoking {@link #tryAcquireShared} until success or the thread 58 * is interrupted. 59 * @param arg the acquire argument 60 * This value is conveyed to {@link #tryAcquireShared} but is 61 * otherwise uninterpreted and can represent anything 62 * you like. 63 * @throws InterruptedException if the current thread is interrupted 64 65 以共享模式获取,如果中断则中止。首先检查中断状态,然后至少调用一次{@link tryacquireshared},成功返回。否则线程排队,可能重复阻塞和解除阻塞,调用{@link tryacquireshared}直到成功或线程被打断了。 66 */ 67 public final void acquireSharedInterruptibly(int arg) 68 throws InterruptedException { 69 if (Thread.interrupted()) 70 throw new InterruptedException(); 71 if (tryAcquireShared(arg) < 0) 72 doAcquireSharedInterruptibly(arg); 73 } 74 75 public static boolean interrupted() { 76 return currentThread().isInterrupted(true); 77 } 78 79 protected int tryAcquireShared(int arg) { 80 throw new UnsupportedOperationException(); 81 } 82 83 /** 84 * Acquires in shared interruptible mode. 85 * @param arg the acquire argument 86 */ 87 private void doAcquireSharedInterruptibly(int arg) 88 throws InterruptedException { 89 final Node node = addWaiter(Node.SHARED); 90 boolean failed = true; 91 try { 92 for (;;) { 93 final Node p = node.predecessor(); 94 if (p == head) { 95 int r = tryAcquireShared(arg); 96 if (r >= 0) { 97 setHeadAndPropagate(node, r); 98 p.next = null; // help GC 99 failed = false; 100 return; 101 } 102 } 103 if (shouldParkAfterFailedAcquire(p, node) && 104 parkAndCheckInterrupt()) 105 throw new InterruptedException(); 106 } 107 } finally { 108 if (failed) 109 cancelAcquire(node); 110 } 111 }
View Code
 1 /**
 2  * Decrements the count of the latch, releasing all waiting threads if
 3  * the count reaches zero.
 4  *
 5  * 

If the current count is greater than zero then it is decremented. 6 * If the new count is zero then all waiting threads are re-enabled for 7 * thread scheduling purposes. 8 * 9 *

If the current count equals zero then nothing happens. 10 11 减少闭锁的计数,如果计数到达0,释放所有等待的线程。 12 如果当前计数大于0,则将其递减。 13 如果新的计数是0,则所有等待的线程为了线程调度的目的被重新启用 14 如果当前计数等于0,则什么也不发生。 15 */ 16 public void countDown() { 17 sync.releaseShared(1); 18 } 19 20 /** 21 * Releases in shared mode. Implemented by unblocking one or more 22 * threads if {@link #tryReleaseShared} returns true. 23 * 24 * @param arg the release argument. This value is conveyed to 25 * {@link #tryReleaseShared} but is otherwise uninterpreted 26 * and can represent anything you like. 27 * @return the value returned from {@link #tryReleaseShared} 28 */ 29 public final boolean releaseShared(int arg) { 30 if (tryReleaseShared(arg)) { 31 doReleaseShared(); 32 return true; 33 } 34 return false; 35 } 36 37 /** 38 * Release action for shared mode -- signal successor and ensure 39 * propagation. (Note: For exclusive mode, release just amounts 40 * to calling unparkSuccessor of head if it needs signal.) 41 */ 42 private void doReleaseShared() { 43 /* 44 * Ensure that a release propagates, even if there are other 45 * in-progress acquires/releases. This proceeds in the usual 46 * way of trying to unparkSuccessor of head if it needs 47 * signal. But if it does not, status is set to PROPAGATE to 48 * ensure that upon release, propagation continues. 49 * Additionally, we must loop in case a new node is added 50 * while we are doing this. Also, unlike other uses of 51 * unparkSuccessor, we need to know if CAS to reset status 52 * fails, if so rechecking. 53 */ 54 for (;;) { 55 Node h = head; 56 if (h != null && h != tail) { 57 int ws = h.waitStatus; 58 if (ws == Node.SIGNAL) { 59 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 60 continue; // loop to recheck cases 61 unparkSuccessor(h); 62 } 63 else if (ws == 0 && 64 !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 65 continue; // loop on failed CAS 66 } 67 if (h == head) // loop if head changed 68 break; 69 } 70 }

View Code
 1 /**
 2  * Implements timed condition wait.
 3  * 
    4 *
  1. If current thread is interrupted, throw InterruptedException. 5 *
  2. Save lock state returned by {@link #getState}. 6 *
  3. Invoke {@link #release} with 7 * saved state as argument, throwing 8 * IllegalMonitorStateException if it fails. 9 *
  4. Block until signalled, interrupted, or timed out. 10 *
  5. Reacquire by invoking specialized version of 11 * {@link #acquire} with saved state as argument. 12 *
  6. If interrupted while blocked in step 4, throw InterruptedException. 13 *
  7. If timed out while blocked in step 4, return false, else true. 14 *
15 */ 16 public final boolean await(long time, TimeUnit unit) 17 throws InterruptedException { 18 if (unit == null) 19 throw new NullPointerException(); 20 long nanosTimeout = unit.toNanos(time); 21 if (Thread.interrupted()) 22 throw new InterruptedException(); 23 Node node = addConditionWaiter(); 24 int savedState = fullyRelease(node); 25 long lastTime = System.nanoTime(); 26 boolean timedout = false; 27 int interruptMode = 0; 28 while (!isOnSyncQueue(node)) { 29 if (nanosTimeout <= 0L) { 30 timedout = transferAfterCancelledWait(node); 31 break; 32 } 33 if (nanosTimeout >= spinForTimeoutThreshold) 34 LockSupport.parkNanos(this, nanosTimeout); 35 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 36 break; 37 long now = System.nanoTime(); 38 nanosTimeout -= now - lastTime; 39 lastTime = now; 40 } 41 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 42 interruptMode = REINTERRUPT; 43 if (node.nextWaiter != null) 44 unlinkCancelledWaiters(); 45 if (interruptMode != 0) 46 reportInterruptAfterWait(interruptMode); 47 return !timedout; 48 }
View Code

 用两把锁实现一个线程等待一组线程执行完毕。可以计算这组线程的执行时间。

package com.citi.test.mutiplethread.demo5;

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch {
    public static void main(String[] args) throws Exception {
        final CountDownLatch gate=new CountDownLatch(1);
        final CountDownLatch gate1=new CountDownLatch(5);
        for(int i=0;i<5;i++){
            final int index=i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        gate.await();
                        System.out.println(Thread.currentThread().getName()+"线程创建");
                        Thread.sleep(index*1000);
                        System.out.println(Thread.currentThread().getName()+"开始运行");
                        gate1.countDown();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    
                }
            }).start();
        }
        System.out.println("主线程执行");
        long startTime=System.currentTimeMillis();
        gate.countDown();
        try {
            System.out.println("主线程等待线程运行");
            gate1.await();
            System.out.println("运行完毕,耗时:"+(System.currentTimeMillis()-startTime));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
View Code

一组线程等待另一组线程执行完毕,再执行。

package com.citi.test.mutiplethread.demo5;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class TestCountDownLatch3 {
    public static void main(String[] args) throws Exception {
        final AtomicInteger integer=new AtomicInteger();
        final CountDownLatch memberGate=new CountDownLatch(10);
        //需要先运行的线程。
        for(int i=0;i<10;i++){
            final int index=i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000*index);
                        System.out.println(Thread.currentThread().getName()+" 运行完");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    for(int j=0;j<5;j++){
                        integer.incrementAndGet();
                    }
                    memberGate.countDown();
                }
            }).start();
        }
        for(int i=0;i<10;i++){
            final int index=i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+"开始运行");
                        //需要等待上面的线程运行完毕再运行 这样就实现了一组线程等待另一组线程的场景
                        memberGate.await();
                        Thread.sleep(index*1000);
                        System.out.println(Thread.currentThread().getName()+" 运行");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}
View Code

用一个CountDownLatch来实现主线程等待一组线程执行完毕。

package com.citi.test.mutiplethread.demo5;

import java.util.concurrent.CountDownLatch;

public class TestCountDownLatch4 {
    public static void main(String[] args) throws Exception {
        final CountDownLatch gate=new CountDownLatch(5);
        for(int i=0;i<5;i++){
            final int index=i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName()+"开始运行");
                        Thread.sleep(index*1000);
                        gate.countDown();
                        System.out.println(Thread.currentThread().getName()+"运行完毕");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    
                }
            }).start();
        }
        gate.await();
        System.out.println("主线程执行");
        Thread.sleep(2000);
        System.out.println("运行完毕");
    }
}
View Code

其实,归根结底,都是通过new CountDownLatch(count)来让等待的线程等待,让需要先执行的线程在执行完毕之后调用latch.countDown()方法。

你可能感兴趣的:(CountDownLatch)