• CountDownLatch,Semaphore,CyclicBarrier使用


    CountDownLatch

    应用:适用于执行某个任务之前必须等到它的前置任务都执行完才可以开始。例如并发测试100个线程同时执行,网站各个模块多线程同时加载数据,当全部加载完成再返回给前端。

    public class CountDownLatchDemo extends Thread {
    
        static CountDownLatch countDownLatch = new CountDownLatch(1); //计数次数
    
        public CountDownLatchDemo(String name) {
            super(name);
        }
    
        @Override
        public void run(){
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThreadName:"+this.getName()+"执行了。");
    
        }
    
        public static void main(String[] args) {
            for (int i=0; i<1000; i++){
                new Thread(new CountDownLatchDemo("线程:"+i)).start();
            }
            countDownLatch.countDown();
        }
    }
    

    分析:内部使用的是共享锁机制

    ​ new CountDownLatch(1) 首先初始化一个数字为1的计数器,赋值给里面的state变量。

    ​ 当调用await()时,首先会尝试判断state是否为0,如果不为0会将该节点封装为一个Node并且插入到AQS队列中,并且无限循环判断state是否为0,如果不为0,则调用LockSupport.park()进行阻塞,如果为0,则唤醒该节点并跳出该线程的阻塞。

    ​ 调用countDown(),将state值减1,在判断是否为0,如果为0则调用LockSupport.unpark()唤醒AQS队列中的线程,如果不为0,则只更新state的值。

    Semaphore

    应用:信号灯,semaphore可以控制同时访问线程的个数,通过acquire()获取一个许可,如果没有就等待,通过release释放一个许可。比如商场就5个车位,但是同时会进来10辆车。

    public class SemaphoreDemo extends Thread {
    
        private Semaphore semaphore;
        private int num;
    
        public SemaphoreDemo(int num, Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }
    
        @Override
        public void run(){
            try {
                semaphore.acquire();
    
                TimeUnit.SECONDS.sleep(2);
                System.out.println(String.format("第%s个线程获取到令牌", num));
    
                semaphore.release();
    
                System.out.println(String.format("第%s个线程释放令牌", num));
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(5);
            for(int i=0; i<10; i++){
                new SemaphoreDemo(i, semaphore).start();
            }
        }
    }
    

    分析:内部使用的是共享锁机制

    ​ new Semaphore(5) 首先初始化5个令牌,赋值给里面的state变量。

    ​ 当调用acquire()时,首先会尝试获取令牌,如果没有成功会将该节点封装为一个Node并且插入到AQS队列中,并且无限循环判断剩下的空位是否大于等于0,如果小于0,则调用LockSupport.park()进行阻塞,否则,则唤醒该节点并跳出该线程的阻塞。

    ​ 调用release(),将令牌值加1,更新成功则调用LockSupport.unpark()唤醒AQS队列中的线程。

    CyclicBarrier

    应用:让一组线程到达一个屏障(也可以叫同步点)时被阻塞,知道最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。

    public class CyclicBarrierDemo extends Thread {
    
        private CyclicBarrier cyclicBarrier;
        private String path;
    
        public CyclicBarrierDemo(CyclicBarrier cyclicBarrier, String path){
            this.cyclicBarrier = cyclicBarrier;
            this.path = path;
        }
    
        @Override
        public void run(){
            try {
                System.out.println(String.format("开始导入路径[%s]的数据", path));
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
           /**
           1,如果指定计数值3,因为某种原因,没有足够的线程(3个)调用cyclicBarrier.await(),那么所有调用await()方法的线程都会阻塞,我们可以通过设置超时时间来接触这种状态await(timeout, unit)
           2,所有线程会等待全部线程到达栅栏之后才会继续执行,并且最后到达的线程会完成 Runnable 的任务。
           */
            CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始执行主任务");
                }
            });
    
            new CyclicBarrierDemo(cyclicBarrier, "路径1").start();
            new CyclicBarrierDemo(cyclicBarrier, "路径2").start();
            new CyclicBarrierDemo(cyclicBarrier, "路径3").start();
    
        }
    
    }
    

    分析:内部使用的是共享锁机制

    ​ new CyclicBarrier(int parties, Runnable barrierAction),将parties赋值给count,代表等count个线程await之后,由最后一个线程来执行这个Runnable。

    ​ 每调用一次await,那么count值就会减1,如果不为0,那么久调用condition.await()方法阻塞,当等于0的时候调用condition.signalAll唤醒所有await阻塞的线程。

    CyclicBarrier 与 CountDownLatch 区别

    • CountDownLatch是计数器,只能使用一次,而CyclicBarrier的计数器提供reset功能,可以多次使用。
    • CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier 阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。
    • CountDownLatch 是计数器, 线程完成一个就记一个, 就像报数一样, 只不过是递减的,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。
  • 相关阅读:
    java 28
    java 28
    java 27
    java 27
    java 27
    java 27
    java 27
    java 27
    java 27
    java 27
  • 原文地址:https://www.cnblogs.com/gaojf/p/12780125.html
Copyright © 2020-2023  润新知