并发编程第四篇

本文介绍了Java并发工具类CountDownLatch、Semaphore的使用场景、源码分析及其与线程池的关系。CountDownLatch用于同步多个线程,Semaphore则能控制并发访问的线程数量。此外,文章探讨了Java线程池的原理和不同类型的线程池,如FixedThreadPool、CachedThreadPool和SingleThreadExecutor,以及它们在任务提交时的行为差异。
摘要由CSDN通过智能技术生成

JUC中提供了几个比较常用的并发工具类,比如CountDownLatch、CyclicBarrier、Semaphore。

CountDownLatch

countdownlatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行。从命名可以解读到countdown是倒数的意思,类似于我们倒计时的概念。
countdownlatch提供了两个方法:

一个是countDown;
一个是await;
countdownlatch初始化的时候需要传入一 个整数,在这个整数倒数到0之前,调用了await方法的程序都必须要等待,然后通过countDown来倒数。

使用案例

 public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch=new CountDownLatch(3);
        new Thread(()->{
            try {
                Thread.sleep(2000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();  //递减

        }).start();

        new Thread(()->{
            try {
                Thread.sleep(2000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();

        }).start();

        new Thread(()->{
            countDownLatch.countDown();
            try {
                Thread.sleep(2000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        countDownLatch.await(); //阻塞
        System.out.println("执行完毕 ");
    }

从代码的实现来看,有点类似join的功能,但是比join更加灵活。CountDownLatch构造函数会接收一个int类型的参数作为计数器的初始值,当调用CountDownLatch的countDown方法时,这个计数器就会减一。
在这里插入图片描述
使用场景
\1. 通过countdownlatch实现最大的并行请求,也就是可以让N个线程同时执行;
\2. 比如应用程序启动之前,需要确保相应的服务已经启动,比如zookeeper,通过原生api连接的地方有用到countDownLatch;

源码分析

CountDownLatch类存在一个内部类Sync,第三篇中提到过,它是一个同步工具,一定继承了 AbstractQueuedSynchronizer。很显然,CountDownLatch实际上是是使得线程阻塞了,既然涉及到阻塞,就一 定涉及到AQS队列。

await
await函数会使得当前线程在countdownlatch倒计时到0之前一直等待,除非线程别中断;从源码中可以得知await 方法会转发到Sync的acquireSharedInterruptibly
方法

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

acquireSharedInterruptibly
这块代码主要是判断当前线程是否获取到了共享锁; AQS有两种锁类型,一种是共享锁,一种是独占锁,在这里用的是共享锁; 为什么要用共享锁,因为CountDownLatch可以多个线程同时通过。

public final void acquireSharedInterruptibly(int arg)throws InterruptedException {
     //判断线程是否中断
     if (Thread.interrupted()) 
           throw new InterruptedException();
      //如果等于0则返回1,否则返回-1,返回-1表示需要阻塞
      if (tryAcquireShared(arg) < 0) 
           doAcquireSharedInterruptibly(arg);
}

在这里,state的意义是count,如果计数器为0,表示不需要阻塞,否则,只有在满足条件的情况下才会被唤醒。

doAcquireSharedInterruptibly
获取共享锁

private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
 
    final Node node = addWaiter(Node.SHARED); //创建一个共享模式的节点添加到队列中  
    boolean failed = true;
    try {
 
        for (;;) { //自旋等待共享锁释放,也就是等待计数器等于0。
 
            final Node p = node.predecessor(); //获得当前节点的前一个节点
 
            if (p == head) {
 
                int r = tryAcquireShared(arg);//就判断尝试获取锁
 
                if (r >= 0) {//r>=0表示计数器已经归零了,则释放当前的共享锁
 
                    setHeadAndPropagate(node, r);
 
                    p.next = null; // help GC
 
                    failed = false;
 
                    return;
 
          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值