CountDownLatch

CountDownLatch的工作原理   

   CountDownLatch在多线程并发编程中充当一个计时器的功能,并且维护一个count的变量,并且其操作都是原子操作,该类主要通过countDown()和await()两个方法实现功能的,首先通过建立CountDownLatch对象,并且传入参数即为count初始值。如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。如果一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操作。比如下面的例子就是有两个操作,一个是读操作一个是写操作,现在规定必须进行完写操作才能进行读操作。所以当最开始调用读操作时,需要用await()方法使其阻塞,当写操作结束时,则需要使count等于0。因此count的初始值可以定为写操作的记录数,这样便可以使得进行完写操作,然后进行读操作。

CountDownLatch的内部类Sync继承了AQS(AbstractQueuedSynchronizer)抽象类,该内部类实现了 tryAcquireShared()方法 和 tryReleaseShared()方法,说明了底层使用了AQS的共享锁

内部维护了AQS的一个int类型的变量state,初始化将该变量设置为传入的int值,使用该并发工具主要使用其中的两个方法:await()和countDown()
调用await()方法的线程会进行一个自旋,当调用countDown()的线程数量达到state个时,调用await()方法的线程将继续运行。
 

CountDownLatch函数列表

// 构造一个用给定计数初始化的 CountDownLatch。
CountDownLatch(int count)
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void await()
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, TimeUnit unit)
// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
void countDown()
// 返回当前计数。
long getCount()
// 返回标识此锁存器及其状态的字符串。
String toString()

await方法

调用await() 方法,来使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断
该方法内部使用 AQS 的 acquireSharedInterruptibly(int arg) 方法
在内部类 Sync 中重写了 tryAcquireShared(int arg) 方法
通过AQS中的getState() 方法,获取同步状态,其值等于计数器的值
如果计数器值不等于 0,则会调用 doAcquireSharedInterruptibly(int arg) 方法,该方法为一个自旋方法会尝试一直去获取同步状态
 

countDown方法

调用countDown() 方法,来改变计数器数量
内部调用AQS的releaseShared() 方法
Sync重写了tryReleaseShared() 方法
释放锁,也就是操作计数器的过程,这里使用到了CAS(compareAndSetState)进行计数更新,若更新失败则进行自旋重试直到成功为止

getCount方法

调用AQS的getState方法获取计数

使用场景

   某一线程在开始运行前等待n个线程执行完毕

例子:五个线程一起加载只用等待五个线程都执行完才能继续执行

package com.it.web;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class ThreadTest {

     CountDownLatch countDownLatch=new CountDownLatch(5);
    public static void main(String[] args) throws InterruptedException {
        ThreadTest threadTest = new ThreadTest();
        threadTest.m();
    }

    Random random=new Random();
    public void m() throws InterruptedException {
        String[] numbers= new String[5];
        for (int i = 0; i <5 ; i++) {
            int index=i;
            new Thread(()->{
                String name = Thread.currentThread().getName();
                for (int j = 0; j <=100; j++) {
                    try {
                        Thread.sleep(random.nextInt(100));
                        numbers[index]=name+"%-"+j;
                        System.out.print("\r" + Arrays.toString(numbers));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }

            },"t"+(i+1)).start();

            countDownLatch.countDown();
        }

     countDownLatch.await();

    }

}

总结


CountDownLatch的作用实际是提供了一种多线程通信的方式
AQS提供了多个线程并发且产生竞态条件时共享变量的安全读写方式,且丰富地提供了共享、排他两种处理竞态线程获取共享资源的方式
CountDownLatch利用AQS的state共享变量作为信号量,await方法会在state不等于0时进行阻塞,countDown方法会操作state共享变量改变这个信号量的值从而来影响整个CountDownLatch的运行
CountdownLatch是通过共享方式对锁进行操作,通过自旋方式进行阻塞等待,通过CAS对共享变量进行更新保证原子性
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值