java并发之CountdownLatch

一:CountdownLatch

1:基本含义

CountDownLatch中count down是倒数的意思,latch则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有一点“三二一,芝麻开门”的感觉。CountDownLatch的作用也是如此,==在构造CountDownLatch的时候需要传入一个整数n,在这个整数“倒数”到0之前,主线程需要等待在门口,而这个“倒数”过程则是由各个执行线程驱动的,每个线程执行完一个任务“倒数”一次。
总结来说,CountDownLatch的作用就是等待其他的线程都执行完任务,必要时可以对各个任务的执行结果进行汇总,然后主线程才继续往下执行。

2:常用方法

  • countDown()和await()。countDown()方法用于使计数器减一,其一般是执行任务的线程调用,await()方法则使调用该方法的线程处于等待状态,其一般是主线程调用。
  • 这里需要注意的是,countDown()方法并没有规定一个线程只能调用一次,当同一个线程调用多次countDown()方法时,每次都会使计数器减一;
  • 另外,await()方法也并没有规定只能有一个线程执行该方法,如果多个线程同时执行await()方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一个锁。
  • 共享锁:是指该锁可被多个线程所持有,允许多个线程同时去获取。

二:主要方法解析

1:countDown()

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

2:AQS中的releaseShared(int arg)

(1):releaseShared(int arg)

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

(2):tryReleaseShared(int releases)

    protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)//如果其计数器就是0的话,那就不用去唤醒其它线程了,直接执行本线程,所以返回false
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;//如果我们的计数器为0了的话,那么就返回true  就可以执行 唤醒其他线程的操作了
            }
        }

(3): doReleaseShared();(唤醒其它线程)

private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
  • 在执行sync.releaseShared(1)方法时,其在调用tryReleaseShared(int)方法时会在无限for循环中设置state属性的值,设置成功之后其会根据设置的返回值(此时state已经自减了一),即当前线程是否为将state属性设置为0的线程,来判断是否执行if块中的代码。
  • doReleaseShared()方法主要作用是唤醒调用了await()方法的线程。需要注意的是,如果有多个线程调用了await()方法,这些线程都是以共享的方式等待被唤醒的,试想,如果以独占的方式等待,那么当计数器减少至零时,就只有一个线程会被唤醒执行await()之后的代码,这显然不符合逻辑。

三:上码应用

1:简单认识CountDownLatch的线程并行

我们定义一个线程池,其可以开启4个线程,我们再定义CountDownLatch(3),也就是一次可以让三个线程并行的的执行,只有这三次线程并行的执行完了之后,才可以让第四个线程执行。


public class TextCountDownLatch {
    public static void main(String[] args) {


        CountDownLatch downLatch = new CountDownLatch(3);
        ExecutorService executorService = Executors.newFixedThreadPool(4);

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"...begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            downLatch.countDown();
            System.out.println(Thread.currentThread().getName()+"...end...." + downLatch.getCount());
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"...begin");
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            downLatch.countDown();
            System.out.println(Thread.currentThread().getName()+"...end...." + downLatch.getCount() );
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"...begin");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            downLatch.countDown();
            System.out.println(Thread.currentThread().getName()+"...end...."+downLatch.getCount());
        });

        executorService.submit(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"...waiting");
                downLatch.await();
                System.out.println(Thread.currentThread().getName()+"...start");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });



    }
}

在这里插入图片描述

2:CountDownLatch的应用

(1):前言

在我们打开游戏的时候,往往不是直接进入的游戏的,而是先等游戏加载完,然后再进行进入。然而这个加载的过程就是并行的执行多条线程。

(2):上码

public static void main(String[] args) throws InterruptedException {

        ExecutorService service = Executors.newFixedThreadPool(10);
        CountDownLatch latch = new CountDownLatch(10);
        String[] all = new String[10];
        Random random = new Random();


        for (int j = 0; j < 10; j++) {
            int k = j;
            service.submit(()->{
                for (int i = 0; i <= 100 ; i++) {
                    try {
                        Thread.sleep(random.nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    all[k] = i +"%";
                    System.out.print("\r"+Arrays.toString(all));
                }
                latch.countDown();//每加载完一个线程的话,那么就计数减一,
            });
        }

        latch.await();//等待那十个线程结束后唤醒
        System.out.println("/n"+"游戏开始");
        service.shutdown();//线程池关闭

    }

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天向上的菜鸡杰!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值