• java多线程并发之CountDownLatch


    CountDownLatch : 主线程同时启动所有子线程,等待所有子线程都执行完毕,才重新执行主线程;其内部的计数器继承了AQS,AQS内部维持了一个volatile变量 state,用来表示同步状态,

    (1) CountDownLatch(int count) 初始化计数器:当执行CountDownLatch downCountDownLatch = new CountDownLatch(5)时,已初始化一个基于AQS的同步队列,并把传进来的计数器赋给AQS队列的stat【setState(count)】,stat的值也表示一个针对CountdownLatch当前剩余的计数次数;

    // 继承了AQS
     private static final class Sync extends AbstractQueuedSynchronizer {...}
    /**
     * 创建一个值为count的计数器 
     */ 
    public CountDownLatch(int count) {
    	if (count < 0) throw new IllegalArgumentException("count < 0");
    	// Sync 是CountDownLatch的一个内部类
    	this.sync = new Sync(count);
    }
    
    Sync(int count) {
        // 继承自AQS
        setState(count);
    }
    

    (2)void await():阻塞当前进程

    当调用await的时候,当前线程会被阻塞,同时加入到AQS阻塞队列,实际上是调用了AQS的acquireSharedInterruptibly方法,当线程调用了await方法时,该线程将被阻塞,直到下面两种情况之一:
    (a)当所有线程都调用了countDown方法,也就是计数值为0时,
    (b)当前线程的interrupted()方法被其他线程调用,就会抛出InterruptedException异常

    /**
     * 阻塞当前进程,将当前进程加入阻塞队列
     */
    public void await() throws InterruptedException {
    	sync.acquireSharedInterruptibly(1);
    }
    
    //AQS获取共享资源时可被中断的方法,
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //如果线程被中断则抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //查看当前计数器值是否为0,
        if (tryAcquireShared(arg) < 0)
    		// 调用AQS的doAcquireSharedInterruptibly方法让当前线程阻塞
            // 实际上会构建阻塞队列的双向链表,挂起当前线程
            doAcquireSharedInterruptibly(arg);
    }
    
    //sync类实现的AQS的接口
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }
    线程获取资源时可以被中断,并且获取的资源是共享资源.
    

    (3)boolean await(long timeout, TimeUnit unit)

    和public void await()区别仅在于当线程阻塞时间超过timeout时,会返回false,计数器为0时,则返回true,发生中断会抛出异常,而void await()方法没有返回值

    (4)void countDown():计数器减1,当计数值为0时,则释放所有因调用await方法阻塞的线程,否则什么都不做

    public void countDown() {
        //委托sync调用AQS的方法
        sync.releaseShared(1);
    }
    
    //AQS的方法
    public final boolean releaseShared(int arg) {
        //调用sync实现的tryReleaseShared
        if (tryReleaseShared(arg)) {
            //AQS的释放资源方法
            doReleaseShared();
            return true;
        }
        return false;
    }
    // Syc 实现的tryReleaseShared
    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;
            // 调用AQS的cas
    		if (compareAndSetState(c, nextc))
    			return nextc == 0;
    	}
    }
    

    调用countDown()方法时是原子性递减AQS的state状态值,CountDownLatch中的Sync会优先尝试修改state的值,来获取同步状态。例如,如果某个线程成功的将state的值从0修改为1,表示成功的获取了同步状态。 这个修改的过程是通过CAS完成的,所以可以保证线程安全。

    反之,如果修改state失败,则会将当前线程加入到AQS的队列中,并阻塞线程。

    当CountDownLatch因异常导致计数值不能正常递减,最终到达0时,相应实例上的线程就会一直处于WAITING状态,有两种方式可避免出现这种现象:(a)调用countDown()方法时,尽量放在finally代码块内;(b)使用CountDownLatch.await(long,TimeUnit)方法,超时之后等待线程会自动唤醒

    (5)long getCount():当前的计数器值

    /**
     * 返回当前计数器的值
     */
    public long getCount() {
        return sync.getCount();
    }
    // 其内部还是调用了 AQS 的getState来获取state值
    int getCount() {
        return getState();
    }
    

    CountDownLatch 是线程安全的

    CountDownLatch和join的区别

    使用join方法必须等待多个线程执行完毕之后才能解除阻塞状态,而CountDownLatch相对灵活,可以通过调用countDown方法来减少计数,唤醒被阻塞的线程

    参考的实例

    package com.example.lettcode.concurrent;
    
    import cn.hutool.core.util.RandomUtil;
    
    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Class CountDownLatchDemo
     * @Description TODO
     * @Author
     * @Date 2021/4/8
     **/
    public class CountDownLatchDemo {
        public static void main(String[] args) {
            int startCount = 1;
            CountDownLatch startCountDownLatch = new CountDownLatch(startCount);
    
            int downCount = 5;
            CountDownLatch downCountDownLatch = new CountDownLatch(downCount);
    
            for (int i = 0; i < downCount; i++) {
                new Thread(new WorkerRunnable(startCountDownLatch, downCountDownLatch), "线程:" + i).start();
            }
            try {
                // 休眠1s ,保证所有的线程都已经调用了await方法,进入阻塞状态
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println("所有线程都已启动!!!");
            // 唤醒所有阻塞的子线程
            startCountDownLatch.countDown();
            System.out.println("等待所有线程执行结束!!!");
            try {
                // 此处是为了让主线程阻塞,等待所有子线程执行完毕
                downCountDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("所有任务都已执行结束!!!");
        }
    
        static class WorkerRunnable implements Runnable {
    
            private CountDownLatch startCountDownLatch;
            private CountDownLatch downCountDownLatch;
    
            public WorkerRunnable(CountDownLatch startCountDownLatch, CountDownLatch downCountDownLatch) {
                this.startCountDownLatch = startCountDownLatch;
                this.downCountDownLatch = downCountDownLatch;
            }
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " 已经准备好。。。");
                try {
                    // 等待所有子线程都准备完毕再开始
                    startCountDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                childThreadWork();
                try {
                    downCountDownLatch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            private void childThreadWork() {
                System.out.println(Thread.currentThread().getName() + "开始执行");
                try {
                    TimeUnit.SECONDS.sleep(RandomUtil.randomInt(5));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

  • 相关阅读:
    Pet Shop 4.0 详细解析(转) 沧海一粟
    如何制作Bat批处理文件 沧海一粟
    iOS开发Icon图标设置 (转) 沧海一粟
    Android金背大刀之ToggleButton之稍息立正
    Android碧水剑之DatePickerDialog、TimePickerDialog之岁月如梭
    平衡边界作业算法并发仿真测试基于三层架构的Web系统的基准性能
    Android鸳鸯刀之DatePicker、TimePicker之明年今日
    Android应用性能优化整体策略
    Android应用开发之性能测试之TraceView
    平衡边界作业算法并发仿真测试网络存储系统的响应时间
  • 原文地址:https://www.cnblogs.com/fyusac/p/14706373.html
Copyright © 2020-2023  润新知