并发(七):闭锁应用与其实现原理

闭锁是一种线程同步工具,用于同步相关线程达到相关状态(并不一定要结束),然后再执行下一步操作,所以在所有的状态满足之前,调用await的线程将一直阻塞,相对于CyclicBarrier,闭锁更强调信号的同步。当然了,闭锁也能达到CyclicBarrier的效果。

假定有个任务,需要四个线程的任务都完成后才能继续执行,为了更便于理解,这里改为任务完成一半,任务类的逻辑如下:

static class Task implements Runnable {

    private CountDownLatch latch;

    public YieldTest(CountDownLatch latch) {
        super();
        this.latch = latch;
    }

    @Override
    public void run() {
        int times = 100;
        while(times > 50) {
            times --;
        }
        System.out.println(Thread.currentThread().getName() + " Half of Work!");
        //  任务完成一半就通知其他线程
        latch.countDown();
        while(times > 0) {
            times --;
        }
        System.out.println(Thread.currentThread().getName() + " Complete Work!");
    }       

}

调用者的代码示例如下:

final int count = 4;
CountDownLatch latch = new CountDownLatch(count);
long now = System.nanoTime();
for(int i = 0; i < count; i ++) {
    new Thread(new YieldTest(latch)).start();;
}
//  将会一直阻塞,直到接收到所有的countDown通知
latch.await();
long end = System.nanoTime();

最后的输出结果如下,可见在任务完成一半后,闭锁就已经到达了结束状态,主线程可以继续运行:

Thread-0 Half of Work!
Thread-2 Half of Work!
Thread-3 Half of Work!
Thread-1 Half of Work!
Thread-3 Complete Work!
Thread-2 Complete Work!
Thread-0 Complete Work!
1670248
Thread-1 Complete Work!

闭锁、信号量都是基于AbstractQueuedSynchronizer进行实现,所以在到达结束状态之前,闭锁一直处于阻塞状态,每次计数时,就判断是否已到了终止状态,从而释放正在等待的线程,具体实现如下:

static class MyCountDownLatch extends AbstractQueuedSynchronizer {

    public void countDown() {
        this.releaseShared(1);
    }

    public void await() {
        this.acquireShared(1);
    }

    /**
     * @param counter
     * 初始化可以同步的线程数量
     */
    public MyCountDownLatch(int counter) {
        super();
        this.setState(counter);
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireShared(int)
     */
    @Override
    protected int tryAcquireShared(int arg) {
        return getState() == 0 ? 1 : -1;
    }

    /* (non-Javadoc)
     * @see java.util.concurrent.locks.AbstractQueuedSynchronizer#tryReleaseShared(int)
     */
    @Override
    protected boolean tryReleaseShared(int arg) {
        for(;;) {
            int state = this.getState();
            if(state == 0) {
                return false;
            }
            int nextState = state - 1;
            //  如果这次设置失败,则继续检测
            //  不使用锁的另外一种解决办法
            if(compareAndSetState(state, nextState)) {
                return nextState == 0;
            }               
        }
    }       

}

在上面的例子中,state状态变量与执行的操作(tryAcquireShared、tryReleaseShared)并没有直接的关系,只要满足方法返回结果的要求即可。

除此之外,上面还提供了一种不需要加锁而同步状态数据的方法,即充分利用状态变量的原子操作特性(主要是CAS操作),不停地进行自旋,从而达到检测与更新数据的目标。

结论

利用闭锁可以使一个或多个线程等待一组事件的发生,同时它还包含一个计数器,当计数器到达零时,表示所有需要等待的事情都已经发生。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值