zoukankan      html  css  js  c++  java
  • CountDownLatch源码分析

    一 使用场景

      在网上找了个例子,这个例子很好地说明了 CountDownLatch 的用法,概括地说就是主线程等待子线程执行完了,主线程接着执行

    public class CountDownLatchTest {
    
        public static void main(String[] args) {
            final CountDownLatch latch = new CountDownLatch(2);
            System.out.println("主线程开始执行…… ……");
            //第一个子线程执行
            ExecutorService es1 = Executors.newSingleThreadExecutor();
            es1.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                        System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    latch.countDown();
                }
            });
            es1.shutdown();
    
            //第二个子线程执行
            ExecutorService es2 = Executors.newSingleThreadExecutor();
            es2.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
                    latch.countDown();
                }
            });
            es2.shutdown();
            System.out.println("等待两个线程执行完毕…… ……");
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("两个子线程都执行完毕,继续执行主线程");
        }
    }

    二 代码概况

      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;
                }
            }
        }
    
        private final Sync sync;

    CountDownLatch的构造方法如下

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

    用户传进去的count就赋值给AQS的state

    三  await源码分析

    public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }

      之前分析过共享锁的代码,共享锁的主要逻辑还是要看用户实现的  tryAcquireShared 

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

    如果state不是0的话,返回-1,这样主线程就会进到队列中排队,并把自己阻塞起来,只能等待其他线程的唤醒

    四 CountDown源码分析

    每次调用 countDown() 固定是释放一个共享资源

     public void countDown() {
            sync.releaseShared(1);
        }
    protected boolean tryReleaseShared(int releases) {
                // Decrement count; signal when transition to zero
                for (;;) {
                    int c = getState();
                    if (c == 0)//如果已经是0,那就没法释放了,返回false,这样就不会执行unpark方法
                        return false;
                    int nextc = c-1;
                    if (compareAndSetState(c, nextc))//减一成功后比较state是不是0,如果返回true
                        return nextc == 0;
                }
            }

    如果state == 0,那么  tryReleaseShared 返回true,这样就会执行unpark的逻辑,这样主线程被唤醒,代码就可以继续执行了

    五 总结

      CountDownLatch的源码不多,逻辑也不复杂,但是可以很好的看看如果通过继承AQS实现自己的逻辑。

  • 相关阅读:
    委托的说明和举例
    用C#编写获取远程IP,MAC的方法
    200个Gmail邀请,要的请留下邮箱地址
    .NET中各种数据库连接大全
    .net中何有效的使用Cache
    55种网页常用小技巧(javascript) (转)
    一个WEB项目安装包,自动配置数据库,config文件和虚拟目录。。(转)
    windows xp sp2后所有更新
    C#反射实例(转)
    可扩展的应用程序:新增功能时无须重新编译
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14030995.html
Copyright © 2011-2022 走看看