• 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore


    前言

    JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch、CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行介绍。

    CountDownLatch

    CountDownLatch的主要作用是利用计数来保证线程的执行顺序(我自己的理解),有点像倒计时,当计数为0时某个线程才能开始执行。

    CountDownLatch的主要方法很简单易用,包括:

    • CountDownLatch(int count) : 构造方法,需要传入计数的初始值
    • void await() : 调用者线程会被挂起,直到计数为0时才能执行
    • boolean await(long timeout, TimeUnit unit) : 同上,但是加入了超时参数,如果超时了计数还不为0,也会照样执行,避免了一直阻塞
    • void countDown() : 计数减一

    示例代码:CountDownLatchTest.java

    package juc.util;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * Created by puyangsky on 2017/1/7.
     */
    public class CountDownLatchTest {
        public static void main(String[] args) {
            final CountDownLatch latch = new CountDownLatch(2);
    		
    		//用了匿名类创建线程
            new Thread() {
                public void run() {
                    try {
                        System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                        Thread.sleep(2000);
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                        latch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
    
            new Thread() {
                public void run() {
                    try {
                        System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                        Thread.sleep(2000);
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                        latch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
    
            System.out.println("主线程"+Thread.currentThread().getName()+"正在执行");
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程"+Thread.currentThread().getName()+"执行完毕");
        }
    }
    

    结果:

    主线程main正在执行
    子线程Thread-0正在执行
    子线程Thread-1正在执行
    子线程Thread-0执行完毕
    子线程Thread-1执行完毕
    主线程main执行完毕
    

    可以从结果中看到主线程本来是最先执行完,结果需要等两个子线程执行完才结束,就是因为调用了await方法。

    CyclicBarrier

    CyclicBarrier的意思是回环栅栏,作用是让一组线程同时到达某个时间节点。提供的重要方法如下:

    • CyclicBarrier(int parties) : 构造方法,传入线程组的数量
    • CyclicBarrier(int parties, Runnable barrierAction) : 构造方法,传入线程组的数量和当线程达到时间节点后要做的操作(由其中的某一个线程去执行)
    • int await() : 挂起当前线程,直到所有线程组中的线程都完成后继续执行,返回当前线程到达的次序
    • int await(long timeout, TimeUnit unit) : 加了一个超时参数

    示例代码 CyclicBarrierTest.java:

    package juc.util;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    /**
     * Created by puyangsky on 2017/1/7.
     */
    public class CyclicBarrierTest {
        public static void main(String[] args) {
            CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
                @Override
                public void run() {
                    System.out.println("当前线程"+Thread.currentThread().getName());
                }
            });
            for (int i=0;i<5;i++) {
                new Task(barrier).start();
            }
        }
    
        static class Task extends Thread {
            private CyclicBarrier barrier;
    
            public Task(CyclicBarrier barrier) {
                this.barrier = barrier;
            }
    
            public void run() {
                System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                try {
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println("全部线程执行完成");
            }
        }
    }
    

    结果:

    子线程Thread-0正在执行
    子线程Thread-3正在执行
    子线程Thread-2正在执行
    子线程Thread-1正在执行
    子线程Thread-4正在执行
    子线程Thread-3执行完成
    子线程Thread-2执行完成
    子线程Thread-0执行完成
    子线程Thread-1执行完成
    子线程Thread-4执行完成
    当前线程Thread-4
    全部线程执行完成
    全部线程执行完成
    全部线程执行完成
    全部线程执行完成
    全部线程执行完成
    

    可以看到这一组线程是同步的去执行的。

    Semaphore

    Semaphore的意思是信号量,其作用是提供一个许可范围,只有获得了许可才能继续执行。

    • Semaphore(int permits):构造方法,需要传入许可数
    • Semaphore(int permits, boolean fair):fair为true时使用公平锁,false时使用非公平锁,具体在介绍AQS时讲解
    • void acquire():获得许可,可中断
    • void acquireUninterruptibly():不可中断的获取
    • void release():释放许可

    示例 SemaphoreTest.java

    package juc.util;
    
    import java.util.concurrent.Semaphore;
    
    /**
     * Created by puyangsky on 2017/1/7.
     */
    public class SemaphoreTest {
    
        public static void main(String[] args) {
            Semaphore semaphore = new Semaphore(5);
            for (int i = 0;i < 10;i++) {
                new Worker(i, semaphore).start();
            }
        }
    
        static class Worker extends Thread {
            private Semaphore semaphore;
            private int num;
    
            public Worker(int num, Semaphore semaphore) {
                this.semaphore = semaphore;
                this.num = num;
            }
    
            public void run() {
                try {
    
                    semaphore.acquire();
                    System.out.println("工人" + (this.num + 1) + "占用一个机器在生产...");
                    Thread.sleep(2000);
    
                    System.out.println("工人" + (this.num + 1) + "释放一个机器...");
                    semaphore.release();
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    

    结果

    工人1占用一个机器在生产...
    工人3占用一个机器在生产...
    工人4占用一个机器在生产...
    工人5占用一个机器在生产...
    工人7占用一个机器在生产...
    工人1释放一个机器...
    工人5释放一个机器...
    工人4释放一个机器...
    工人7释放一个机器...
    工人3释放一个机器...
    工人8占用一个机器在生产...
    工人9占用一个机器在生产...
    工人10占用一个机器在生产...
    工人6占用一个机器在生产...
    工人2占用一个机器在生产...
    工人9释放一个机器...
    工人6释放一个机器...
    工人10释放一个机器...
    工人8释放一个机器...
    工人2释放一个机器...
    

    可以看到每次只有五个线程能运行,其他线程只有等待这五个线程释放许可后才能运行。

    参考文章:

  • 相关阅读:
    [C/C++]宽字符与控制台程序
    C# 实现屏幕键盘 (SCREENKEYBOARD)
    c#模拟键盘输入
    窗口玻璃特效,半透明窗口,使用DWM实现Aero Glass效果
    DMRS、DRS、SRS、CRS各自作用区别
    LTE的9种传输模式
    在4G通讯技术中什么是ZC根序列,ZC根序列规划的目的和原则是什么?
    為何LTE要先偵測PSS然後再偵測SSS 转自C114
    PSS和SSS用户小区接入的同步过程
    LTE PCI MOD3 规划
  • 原文地址:https://www.cnblogs.com/puyangsky/p/6260404.html
Copyright © 2020-2023  润新知