juc学习笔记四-CountDownLatch和CyclicBarrier

参考:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-countdownlatch.html

1 CountDownLatch

CountDownLatch没有实现其他接口,其仍然依赖AQS实现。在创建CountDownLatch时,需要制定当前已经被申请的锁资源数量。

CountDownLatch的构造方法:

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

CountDownLatch有两个核心方法需要关注:
await:等待CountDownLatch的状态为0

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);//调用AQS中的申请共享资源方法
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)//调用子类的具体实现,尝试申请资源
            doAcquireSharedInterruptibly(arg);//调用AQS中方法,以自旋的形式申请资源
    }
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);//不停尝试申请资源
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

countDown:CountDownLatch的状态-1

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

调用AQS的释放共享锁资源

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

可以看出两个方法都依赖于内部类实现:

    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;//当资源为0时申请资源成功
        }
        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;
            }
        }
    }

例子:
一个线程T1向list中增加元素,一个线程T2在list个数为5时发出警告:

著作权归https://pdai.tech所有。
链接:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-countdownlatch.html

public class T3 {

   volatile List list = new ArrayList();

    public void add(int i){
        list.add(i);
    }

    public int getSize(){
        return list.size();
    }


    public static void main(String[] args) {
        T3 t = new T3();
        CountDownLatch countDownLatch = new CountDownLatch(1);

        new Thread(() -> {
            System.out.println("t2 start");
           if(t.getSize() != 5){
               try {
                   countDownLatch.await();
                   System.out.println("t2 end");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        },"t2").start();

        new Thread(()->{
            System.out.println("t1 start");
           for (int i = 0;i<9;i++){
               t.add(i);
               System.out.println("add"+ i);
               if(t.getSize() == 5){
                   System.out.println("countdown is open");
                   countDownLatch.countDown();
               }
           }
            System.out.println("t1 end");
        },"t1").start();
    }

}

可以这样理解:创建CountDownLatch时,已经有一个资源被分配出去了。所以当T2遇到list数量不是5的时候就阻塞,等到T1加入元素到list数量等于5的时候,唤醒了T2进行告警。

2 CyclicBarrier

CyclicBarrier依赖ReentrantLock实现
核心方法:

    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;//隔离墙

            if (g.broken)//隔离墙已经被突破
                throw new BrokenBarrierException();

            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count;//当前正在等待的线程数量-1
            if (index == 0) {  // 已经没有线程在等待了
                boolean ranAction = false;
                try {
                //执行任务
                    final Runnable command = barrierCommand;
                    if (command != null)
                        command.run();
                    ranAction = true;//执行完成
                    nextGeneration();
                    return 0;
                } finally {
                    if (!ranAction)
                        breakBarrier();
                }
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await();//堵塞当前线程 给其他正在等待的线程运行
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }

                if (g.broken)//已经被突破
                    throw new BrokenBarrierException();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();//唤醒所有正在等待的线程
        // set up next generation
        count = parties;//进入下一个阶段所以cout返回初始值
        generation = new Generation();//新屏障
    }
    private void breakBarrier() {
    //打破屏障初始化锁状态
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

例子:
线程t1,t2和主线程执行完CyclicBarrier 对象的await方法后,barrierAction才开始运行。

著作权归https://pdai.tech所有。
链接:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-cyclicbarrier.html

class MyThread extends Thread {
    private CyclicBarrier cb;
    public MyThread(String name, CyclicBarrier cb) {
        super(name);
        this.cb = cb;
    }
    
    public void run() {
        System.out.println(Thread.currentThread().getName() + " going to await");
        try {
            cb.await();
            System.out.println(Thread.currentThread().getName() + " continue");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class CyclicBarrierDemo {
    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        CyclicBarrier cb = new CyclicBarrier(3, new Thread("barrierAction") {
            public void run() {
                System.out.println(Thread.currentThread().getName() + " barrier action");
                
            }
        });
        MyThread t1 = new MyThread("t1", cb);
        MyThread t2 = new MyThread("t2", cb);
        t1.start();
        t2.start();
        System.out.println(Thread.currentThread().getName() + " going to await");
        cb.await();
        System.out.println(Thread.currentThread().getName() + " continue");

    }
}

3 CountDownLatch和CyclicBarrier的区别

CountDownLatch是一次性的,CyclicBarrier是可复用的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值