通过跑步彻底搞懂CountDownLatch、CyclicBarrier、Semaphore

CountDownLatch:这个类使一个线程等待其他线程各自执行完毕后再执行。
CyclicBarrier:这个类使所有线程都等待完成后才会继续下一步行动。
Semaphore:这个类是控制线程的并发数量

下面通过我们熟悉的跑步运动来做比喻,让你轻松的明白他们之间的差别:

CountDownLatch例子:裁判等运动员

package com.dada.test;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * @Description: CountDownLatch
 * 一个线程等待其他几个线程执行完毕之后执行:裁判等待运动员
 * 打个比喻:短跑比赛的时候,【裁判员】(等待线程)需要等待【所有运动员】(其他线程)都准备完毕之后才发令
 */
public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(()->{
            System.out.println("运动员1在准备");
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
            System.out.println("运动员1准备完毕");
        }).start();
        new Thread(()->{
            System.out.println("运动员2在准备");
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
            System.out.println("运动员2准备完毕");
        }).start();
        new Thread(()->{
            System.out.println("裁判在等待运动员做准备工作");
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("裁判发令");
        }).start();
    }

}

CountDownLatch使用场景:计算任务拆分

当某个处理运算量很大时,可以将该运算拆分成多个子任务,等待所有的子任务完成之后,父任务拿到所有子任务的运算结果进行汇总

CyclicBarrier例子:运动员之间相互等待

package com.dada.test;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @Description: CyclicBarrier
 * 多个线程相互等待:运动员之间相互等待
 * 打个比喻:短跑运动员开跑的动作是在同一时刻进行的,需要等到所有对手都准备好了才能一起出发
 * 同时这个CyclicBarrier可以通过reset方法来重用
 */
public class CyclicBarrierTest {
    public static void main(String[] args) {
        run();
        reRun();
    }

    public static void run() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                try {
                    Thread.sleep(new Random().nextInt(3)*1000);
                    System.out.println(Thread.currentThread().getName() + ",准备好了");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ",开跑");
            }, "运动员" + i).start();
        }

    }

    public static void reRun() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                try {
                    Thread.sleep(new Random().nextInt(3)*1000);
                    System.out.println(Thread.currentThread().getName() + ",准备好了");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ",开跑");
            }, "中国运动员" + i).start();
        }

        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("========下一波开始准备=========");
        cyclicBarrier.reset();

        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                try {
                    Thread.sleep(new Random().nextInt(3)*1000);
                    System.out.println(Thread.currentThread().getName() + ",准备好了");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ",开跑");
            }, "美国运动员" + i).start();
        }
    }

}

CyclicBarrier使用场景:

和CountDownLatch的使用场景十分相似,它可以用于多线程合并最终计算结果。它可以在rest之后重用,而CountDownLatch不能重用。
关于CyclicBarrier还有个例子,可以在线程达到屏障数的时候,可以指定一个线程,让它优先执行指定的线程。

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
        log.info("callback is running!!!");
});//当屏障内的线程突破5个时才允许其继续执行

Semaphore:接力跑只能一个人在跑

package com.dada.test;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;

/**
 * @Description: SemaPhore 信号量
 * 控制并发:接力跑每次只有一个人能跑
 * 打个比喻:这次是接力跑,每一个队在比赛期间只有1个人可以跑,其他队友只能等待
 */
public class SemaphoreTest {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                try {
                    Thread.sleep(new Random().nextInt(10)*1000);
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + ",获取到跑步锁了,开始跑");
                    Thread.sleep(new Random().nextInt(10)*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
                System.out.println(Thread.currentThread().getName() + ",跑步完了,释放锁了");
            }, "运动员" + i).start();
        }
    }

}

Semaphore使用场景:

比如数据库连接池允许的最大连接数为10,如果同时超过10个线程访问数据库资源,则会导致异常,此时就需要信号量来控制。

你可能感兴趣的:(通过跑步彻底搞懂CountDownLatch、CyclicBarrier、Semaphore)