一直都只了解过countdownlatch,近期在看redission 实现分布式锁的实现的时候发现了Semaphore。
countDownLatch可以作为计数器使用,比如支付,支付需要同时调用很多三方服务,我们为了提升接口的速度,使用多线程同时去获取库存,订单,余额等信息,全部执行完成后再调用真正的下单接口。
执行顺序: 主线程 -》子线程 -》主线程
CountDownLatch 使一个线程A或是组线程A等待其它线程执行完毕后,一个线程A或是组线程A才继续执行
package com.example.springbootmbplus.demo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName: CountDownLatchDemo
* @Description: CountDownLatch demo
* @author: godShan
* @Date: 2020/6/15 16:29
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
//老板等待员工完成工作,开始检查任务
//老板手下有5个员工
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
System.out.println("老板给员工" + (i + 1) + "分配任务");
executorService.execute(new Worker(countDownLatch));
}
//老板等待通知开始检查
countDownLatch.await();
executorService.shutdown();
//老板开始检查工作
System.out.println("老板开始检查工作");
}
/**
* 员工
*/
static class Worker implements Runnable {
private CountDownLatch countDownLatch;
public Worker(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println("员工开始工作 ----------------------");
countDownLatch.countDown();
System.out.println("员工完成工作 ----------------------");
}
}
}
输出结果:
老板给员工1分配任务
老板给员工2分配任务
老板给员工3分配任务
老板给员工4分配任务
老板给员工5分配任务
员工开始工作 ----------------------
员工开始工作 ----------------------
员工完成工作 ----------------------
员工完成工作 ----------------------
员工开始工作 ----------------------
员工完成工作 ----------------------
员工开始工作 ----------------------
员工完成工作 ----------------------
员工开始工作 ----------------------
员工完成工作 ----------------------
老板开始检查工作
CyclicBarrier主要用来做计数,但是它和CountDownLatch不同。它是通过await方法阻塞子线程,等所有的子线程数量到达后,执行回调任务后子线程再去执行await 之后的代码逻辑。
执行顺序: 主线程 -》子线程 -》主线程的回调任务 -》子线程await阻塞后的代码逻辑
CyclicBarrier:一组线程使用await()指定barrier,所有线程都到达各自的barrier后,再同时执行各自barrier下面的代码
package com.example.springbootmbplus.demo;
import lombok.SneakyThrows;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName: CyclicBarrierDemo
* @Description: cyclicBarrier demo
* @author: godShan
* @Date: 2020/6/15 15:33
*/
public class CyclicBarrierDemo2 {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
//老板等待员工完成工作,开始检查任务
//老板手下有5个员工
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
//老板等待通知开始检查
//老板开始检查工作
System.out.println("老板开始检查工作");
});
for (int i = 0; i < 5; i++) {
System.out.println("老板给员工" + (i + 1) + "分配任务");
executorService.execute(new Worker(cyclicBarrier));
}
executorService.shutdown();
}
/**
* 员工
*/
static class Worker implements Runnable {
private CyclicBarrier cyclicBarrier;
public Worker(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@SneakyThrows
@Override
public void run() {
System.out.println("员工开始工作 ----------------------");
cyclicBarrier.await();
System.out.println("员工完成工作 ----------------------");
}
}
}
输出结果:
老板给员工1分配任务
老板给员工2分配任务
老板给员工3分配任务
老板给员工4分配任务
老板给员工5分配任务
员工开始工作 ----------------------
员工开始工作 ----------------------
员工开始工作 ----------------------
员工开始工作 ----------------------
员工开始工作 ----------------------
老板开始检查工作
员工完成工作 ----------------------
员工完成工作 ----------------------
员工完成工作 ----------------------
员工完成工作 ----------------------
员工完成工作 ----------------------
可以看到 CountDownLatch和CyclicBarrier的区别
1.CountDownLatch的countDown不会去阻塞线程;CyclicBarrier调用await会阻塞线程直到回调的方法执行完成
2.CountDownLatch当计数到0时,计数无法被重置;CyclicBarrier计数达到指定值时,计数置为0重新开始
Semaphore主要是用来做限流的。
Semaphore是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源
package com.example.springbootmbplus.demo;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ClassName: SamphoreDemo
* @Description: SemaphoreDemo
* @author: godShan
* @Date: 2020/6/15 14:37
*/
public class SemaphoreDemo {
public static void main(String[] args) throws InterruptedException {
ThreadLocalRandom random = ThreadLocalRandom.current();
ExecutorService executorService = Executors.newCachedThreadPool();
//模拟停车位 100个
Semaphore semaphore = new Semaphore(100);
//模拟需要停车的数量
AtomicInteger needSum = new AtomicInteger(0);
for (int i = 0; i < 2000; i++) {
TimeUnit.SECONDS.sleep(random.nextInt(0,5));
System.out.println("第【" + (i + 1) + "】辆车来到停车场入口");
executorService.execute(() -> {
try {
//占用停车位,内部100-1,如果数量不够了着堵塞
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
//总共停车数
needSum.getAndAdd(1);
System.out.println("今日第【" + needSum.get() + "】进入停车场");
System.out.println("停车场剩余车位: 【" + (semaphore.availablePermits()) + "】");
//todo do something
try {
//模拟逻辑停车时间 5-100秒
TimeUnit.SECONDS.sleep(random.nextInt(50,500));
} catch (InterruptedException e) {
e.printStackTrace();
}
//离开停车场
semaphore.release();
System.out.println("有一辆车离开停车场!");
System.out.println("停车场剩余车位: 【" + (semaphore.availablePermits()) + "】");
});
}
executorService.shutdown();
}
}