Java并发工具类 - CountDownLatch、CyclicBarrier、Semaphore

1. CountDownLatch

实现类似计数器的功能,将一个任务分为多个任务进行执行。

public class MyCountDownLatch {

    //要处理的任务 -倒计时门栓,任务是独立的
    class MyTask implements Callable<String> {
        @Setter
        private String name;
        @Setter
        private CountDownLatch countDownLatch;

        public MyTask(String name, CountDownLatch countDownLatch) {
            this.name = name;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public String call() {
            System.out.println("当前时间:" + new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
            this.countDownLatch.countDown();
            System.out.println(this.countDownLatch.getCount());
            return ("Hello " + name);
        }
    }

    private final int count = 5;
    private CountDownLatch myTaskLatch = new CountDownLatch(count);

    @Test
    public void test() throws InterruptedException {
        //线程池
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
        ExecutorService fixedThreadPool = new ThreadPoolExecutor(10, 20,
                200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
                namedThreadFactory);

        //单个任务
        //Future<String> futureResult = fixedThreadPool.submit(new MyTask("哈哈哈哈"));
        //System.out.println(futureResult.get());

        //多个任务时
        List<Future<String>> resultList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            resultList.add(fixedThreadPool.submit(new MyTask(String.valueOf(i), myTaskLatch)));
        }

        //等待任务完成
        myTaskLatch.await();
        //关闭线程池
        fixedThreadPool.shutdown();
        resultList.forEach(r -> {
            try {
                System.out.println(r.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                System.out.println("任务执行结果发生中断导致失败!");
            }
        });

    }
}

原理

CountDownLatch通过实现AbstractQueuedSynchronizer,内部实现了公平锁类Sync,通过Sync来实现计数。关于AQS的简介可以参考Java并发基础 - AbstractQueuedSynchronizer

2. CyclicBarrier

回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。

public class MyCyclicBarrier {

    //要处理的任务 -循环栅栏,任务相互作用
    class MyTask implements Callable<String> {
        @Setter
        private String name;
        @Setter
        private CyclicBarrier cyclicBarrier;

        public MyTask(String name, CyclicBarrier cyclicBarrier) {
            this.name = name;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public String call() throws BrokenBarrierException, InterruptedException {
            System.out.println("线程" + Thread.currentThread().getName() + "正在写入数据...");
            //线程的集合点
            cyclicBarrier.await();
            System.out.println("栅栏后等待的数量:" + cyclicBarrier.getNumberWaiting());
            return ("Hello " + name);
        }
    }

    private final int count = 5;
    //第二个参数 Runnable barrierAction,就是所有线程执行完之后,再执行的
    private CyclicBarrier myBarrier = new CyclicBarrier(count, () -> System.out.println("栅栏完了,执行其他逻辑..." +
            Thread.currentThread()));

    @Test
    public void test() {

        //1、定义线程池
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
        ExecutorService fixedThreadPool = new ThreadPoolExecutor(10, 20,
                200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(),
                namedThreadFactory);

        //2、定义带返回值的多个任务
        List<Future<String>> resultList = new ArrayList<>(count);
        for (int i = 0; i < 10; i++) {
            resultList.add(fixedThreadPool.submit(new MyTask(String.valueOf(i), myBarrier)));
        }

        //关闭线程池
        //fixedThreadPool.shutdown();
        resultList.forEach(r -> {
            try {
                System.out.println("返回的结果:" + r.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                System.out.println("任务执行结果发生中断导致失败!");
            }
        });
    }

}

CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同;CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时 执行;另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

原理

CyclicBarrier内部通过ReentrantLock进行同步处理。

3. Semaphore

信号量,控制某个资源同时访问的线程个数。和锁有点类似。

public class MySemaphore {

    class Worker implements Runnable {
        private Semaphore semaphore;
        private int count;

        public Worker(Semaphore semaphore, int count) {
            this.semaphore = semaphore;
            this.count = count;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(this + " 占用一个机器在生产...   ");
                System.out.println(this + " 释放机器...   ");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + "-" + count;
        }
    }

    @Test
    public void test() {
        int N = 5;            //工人数
        Semaphore semaphore = new Semaphore(3, true); //机器数目,资源数
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < N; ++i) {
            exec.execute(new Worker(semaphore, i));
        }
        exec.shutdown();
    }
}

运行后可以看到输出:

Worker-2 占用一个机器在生产...   
Worker-1 占用一个机器在生产...   
Worker-2 释放机器...   
Worker-0 占用一个机器在生产...   
Worker-1 释放机器...   
Worker-0 释放机器...   
Worker-4 占用一个机器在生产...   
Worker-3 占用一个机器在生产...   
Worker-4 释放机器...   
Worker-3 释放机器...   

示例代码中有5个工人,但是只有3个机器,也就是说同时最多可以访问3个机器(3个资源),所以5个工人分成了2批。

原理

Semaphore的实现同样借助了AbstractQueuedSynchronizer类,具体的可以参考本人的这篇文章Java并发 - ReentrantLock

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值