1.什么是闭锁

在介绍本篇文章之前,先介绍下什么是闭锁?闭锁是一种同步的东西类,能够推迟线程的进度直到其抵达停止状况,能够把闭锁看作是一扇门,在闭锁抵达完毕状况之前,这扇门一向是封闭的,而且没有任何线程能经过,当抵达完毕状况时,这扇门会翻开允许一切线程经过。需求留意的是,当闭锁抵达完毕状况后,将不会再改动状况,因而这扇门将会永久保持翻开状况。闭锁能够用来保证某些任务直到其他任务都完结才持续履行。

2.闭锁的运用场景

闭锁在开发过程中的用途非常广,总结下来主要有以下几个场景会用到闭锁

2.1.保证某个计算在资源初始后再持续履行

在这种场景下咱们能够运用二元闭锁,二元闭锁包括两个状况,能够用来表明“资源现已被初始化”,而一切需求用到资源R的操作都必须在这个闭锁上等候。例如咱们需求加载一个配置文件,依据配置文件去做一些操作就能够运用这种闭锁的办法;

2.2.保证某个服务在其依靠的一切其他服务都发动之后才发动。

每个服务都有一个相关的二元闭锁。当发动服务S时,将首先在S依靠的其他服务闭锁上等候,在一切依靠的服务都发动后会开释闭锁S,这样其他依靠S的服务才干持续履行。例如在Android的Aidl服务绑定的时分,客户端绑定服务端获取Binder目标时,大约会有个30ms(不精确)的推迟,假如设计SDK的时分,绑定服务端和发动其他调用服务端的操作又是并行的状况下,就会呈现binder为空的状况,这里就能够运用闭锁来完结。具体的完结办法有爱好的话下次单独出一篇文章介绍。

2.3.等候直到某个操作的一切参与者都安排妥当再持续履行

这里举一个我们很熟悉的比如,王者荣耀的匹配,每次匹配都需求你自己的队友和对面的对手在内的一切玩家都安排妥当后才干开端游戏,否则就会呈现少打多的滑稽状况了。这种场景下,当一切的玩家都准备安排妥当时,闭锁将会到达完毕状况。

3. CountDownLatch

CountDownLatch 是闭锁的一种完结,能够在咱们上面的各种场景中运用,它能够使一个或许多个线程等候一组作业产生,闭锁状况包括一个计数器,该计数器被初始化为一个正数,表明需求等候的作业数量,countDown办法递减计数器,表明有一个作业现已产生了,而await办法会一向堵塞直到计数器到达零。这表明一切等候的作业都现已产生,假如计数器的值非零,那么await会一向堵塞直到计数器为零,或许等候中的线程中断,或等候超时

3.1 运用CountDownLatch计算多个作业线程履行的耗时

大致原理是运用两个闭锁,一个开端门,一个完毕门,开端门计数器初始值为1,完毕门的初始值为作业线程的数量,一切作业线程首先要做的作业就是在开端门上等候,然后保证一切线程都安排妥当后才开端履行,每个线程套作的最终一件作业是调用完毕门的countDown办法减一。demo代码如下所示:

  public static long timeTasks(int nThreads, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(nThreads);
        for (int i = 0; i < nThreads; i++){
            Thread t = new Thread(){
                public void run(){
                    try {
                        System.out.println("startGate before========");
                        startGate.await();
                        System.out.println("startGate after========");
                        try{
                            task.run();
                        }finally {
                            endGate.countDown();
                            System.out.println("endGate.countDown()========");
                        }
                    } catch (InterruptedException ignored) {}
                }
            };
            t.start();
        }
        long start = System.nanoTime();
        System.out.println("start========" + start);
        startGate.countDown();
        System.out.println("endGate before========");
        endGate.await();
        long end = System.nanoTime();
        System.out.println("end========" + end);
        return end-start;
    }

留意:上面的代码中,也许读者会发现为啥要在timeTask()中运用闭锁,而不是线程一创建后就立即发动,由于假如在创建线程后就立即发动他们,那么先发动的线程将“抢先”后发动的线程,而且活跃的线程数量会随着时刻的推移而增加或许削减,竞赛程度也在不断产生变化。发动门将使得主线程能够一起开释一切作业线程,而完毕门则使主线程能够等候最终一个线程履行完结,而不是顺序地等候每个线程履行完结