实战Java高并发程序设计之CountDownLatch

CountDownLatch:一个倒数计时器.

这个工具类通常用来控制线程等待,它可以让某一个线程等待直到计时结束,再开始执行.

CountDownLatch允许一个或多个线程等待直到在其他线程中执行的一组操作


如上图所示:主线程会在零界线上等待,检查任务会分别执行,当所有的任务全部都到达零界点以后,主线程才能继续往下执行


基本用法:

CountDownLatch是一个多功能的同步工具,可用于多种用途.

1.如果count初始化为1,它可以作为一个开/关锁存或者是门:所有调用await方法的线程都将一直等到到一个线程调用countDown,将count变成0
2.如果count初始化为n,它可以使一个线程等待,直到N个线程完成某些行为或者某个行为被完成N次

第二种情况分析:

使用Runnable描述每个部分,Runnable执行该部分并在锁存器上倒计时,并将所有Runnables排队到执行程序。
  当所有子部件完成时,协调线程将能够通过等待

伪代码:

class Driver { // ...
	void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     Executor e = ...
     for (int i = 0; i < N; ++i) // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));
       doneSignal.await();           // wait for all to finish
   }
}

class WorkerRunnable implements Runnable {
	private final CountDownLatch doneSignal;
	private final int i;
	WorkerRunnable(CountDownLatch doneSignal, int i) {
		this.doneSignal = doneSignal;
		this.i = i;
	}
	public void run() {
		try {
			doWork(i);
			doneSignal.countDown();
		} catch (InterruptedException ex) {
		} // return;
	}
	void doWork(int i) { 
		//what do you want to do...
	}
}


源码分析:
CountDownLatch也是使用AQS的状态来控制count

/**
     * CountDownLatch的内部类.用 AQS状态来表示计数
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

构造器:

CountDownLatch 初始化的时候必须要给一个count

    /**
     * 构造器
     *
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

await方法:

 await方法在当前的count到达0之前会一直阻塞,当count为0以后,所有的等待的线程都会被释放,并且随后任何的调用await方法都将立即返回
 这是一次性的现象---count不能被重置.如果你想重置count的话,请考虑CyclicBarrier类

    /**
     * 
     *  如果当前计数为零,则此方法立即返回
     *
     *	如果当前计数大于零,则当前线程将被禁用以进行线程调度,并处于休眠状态,直至发生两件事情之一
     *	1.count变成0
     *  2.当前线程被中断
     *
     */
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    /**
     * 同上
     * 指定时间过后直接返回
     */
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

countDown方法:

为了保持内存一致性,在计数器到达0之前,对应的countDown方法都将在await方法之前返回(happen-before原则)

    /**
     * 减少锁存器的计数,如果计数达到零,释放所有等待的线程
     * 如果当前计数大于零,则它将递减。 如果新计数为零,则所有等待的线程都将被重新启用以进行线程调度
     * 如果当前计数等于零,那么没有任何反应
     */
    public void countDown() {
        sync.releaseShared(1);
    }

getCount方法

    /**
     * 返回当前计数.
     * 该方法通常用于调试和测试
     */
    public long getCount() {
        return sync.getCount();
    }


书中demo:

public class CountDownLatchDemo implements Runnable {
    static final CountDownLatch end = new CountDownLatch(10);
    static final CountDownLatchDemo demo=new CountDownLatchDemo();
    @Override
    public void run() {
        try {
            //模拟任务检查
            Thread.sleep(new Random().nextInt(10)*1000);
            System.out.println("check complete");
            end.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(10);
        for(int i=0;i<10;i++){
            exec.submit(demo);
        }
        //等待检查
        end.await();   //等待所有的线程都完成以后才会取消等待,主线程继续
        //发射火箭
        System.out.println("Fire!");
        exec.shutdown();
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值