1. CyclicBarrier
- 可重复使用的屏障:当请求线程达到一定的数量才会放行,放行之后会再次重置待拦截的线程数量
- 代码示例
public static void testCyclicBarrier(){
CyclicBarrier cyclicBarrier =
new CyclicBarrier(4, () -> System.out.println(Thread.currentThread().getName()+":人满开始游戏!"));
class Task implements Runnable{
private CyclicBarrier cyclicBarrier;
public Task(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+":准备就绪");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+":游戏中...");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
for (int i = 0; i < 8; i++) {
try {
TimeUnit.MILLISECONDS.sleep(500);
new Thread(new Task(cyclicBarrier),"线程_"+i).start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程_0:准备就绪
线程_1:准备就绪
线程_2:准备就绪
线程_3:准备就绪
线程_3:人满开始游戏!
线程_3:游戏中...
线程_0:游戏中...
线程_1:游戏中...
线程_2:游戏中...
线程_4:准备就绪
线程_5:准备就绪
线程_6:准备就绪
线程_7:准备就绪
线程_7:人满开始游戏!
线程_7:游戏中...
线程_4:游戏中...
线程_5:游戏中...
线程_6:游戏中...
2. Semaphore
- Semaphore 就是一个信号量,它的作用是单机应用限流,限制某个接口的并发数
- Semaphore的剩余许可量是通过AQS中的state属性进行的记录,获取许可是将该值进行减少,释放许可是将该值进行增加,当没有足够的许可时,线程会加入到阻塞队列中等待其他线程释放许可并唤醒。
- 代码示例
public static void testSemaphore(){
Semaphore semaphore = new Semaphore(3);
class RUN implements Runnable{
private Semaphore semaphore;
public RUN(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
System.out.println("剩余许可证:" + semaphore.availablePermits());
semaphore.acquire();
System.out.println(threadName + ":获取到许可证,执行任务");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(threadName + ":完成任务,释放许可证");
semaphore.release();
}
}
}
for (int i = 0; i < 12; i++) {
new Thread(new RUN(semaphore),"线程"+i).start();
}
}
剩余许可证:3
线程0:获取到许可证,执行任务
剩余许可证:2
线程3:获取到许可证,执行任务
剩余许可证:1
线程4:获取到许可证,执行任务
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
剩余许可证:0
线程3:完成任务,释放许可证
线程0:完成任务,释放许可证
线程7:获取到许可证,执行任务
线程8:获取到许可证,执行任务
线程4:完成任务,释放许可证
线程11:获取到许可证,执行任务
线程7:完成任务,释放许可证
线程11:完成任务,释放许可证
线程1:获取到许可证,执行任务
线程8:完成任务,释放许可证
线程2:获取到许可证,执行任务
线程5:获取到许可证,执行任务
线程2:完成任务,释放许可证
线程1:完成任务,释放许可证
线程5:完成任务,释放许可证
线程9:获取到许可证,执行任务
线程6:获取到许可证,执行任务
线程10:获取到许可证,执行任务
线程6:完成任务,释放许可证
线程9:完成任务,释放许可证
线程10:完成任务,释放许可证
3. CountDownLatch
- CountDownLatch 翻译过来就是倒数的门锁,可以理解为 计数器屏障
- CountDownLatch 的使用场景一:主线程等待,多个子线程完成任务后,进行汇总合并。
public static void testCountDownLatch() throws InterruptedException {
long start = System.currentTimeMillis();
CountDownLatch countDownLatch = new CountDownLatch(5);
class RUN implements Runnable{
private CountDownLatch latch;
public RUN(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"_开始执行");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
} finally {
System.out.println(Thread.currentThread().getName()+"_执行完毕,准备对计数器进行 减1操作");
latch.countDown();
}
}
}
for (int i = 0; i < 5; i++) {
new Thread(new RUN(countDownLatch)).start();
}
System.out.println("...主线程准备等待...");
countDownLatch.await();
System.out.println("...主线程完成等待...");
System.out.println("总花费时间__" + (System.currentTimeMillis()-start));
}
...主线程准备等待...
Thread-0_开始执行
Thread-2_开始执行
Thread-3_开始执行
Thread-1_开始执行
Thread-4_开始执行
Thread-0_执行完毕,准备对计数器进行 减1操作
Thread-1_执行完毕,准备对计数器进行 减1操作
Thread-4_执行完毕,准备对计数器进行 减1操作
Thread-3_执行完毕,准备对计数器进行 减1操作
Thread-2_执行完毕,准备对计数器进行 减1操作
...主线程完成等待...
总花费时间__2030
- CountDownLatch 的使用场景二:多个线程等待:模拟并发,请求一进来就被阻塞,让线程一起执行
public static void testCountDownLatch2() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
class RUN implements Runnable{
private CountDownLatch latch;
public RUN(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"_阻塞等待");
latch.await();
System.out.println(Thread.currentThread().getName()+"_开始执行");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"_执行完毕");
} catch (Exception e){
e.printStackTrace();
}
}
}
for (int i = 0; i < 4; i++) {
new Thread(new RUN(countDownLatch),"线程"+i).start();
}
TimeUnit.SECONDS.sleep(5);
System.out.println("MAIN 线程 使得计数减1... 唤醒所有子线程...");
countDownLatch.countDown();
}
线程2_阻塞等待
线程3_阻塞等待
线程0_阻塞等待
线程1_阻塞等待
MAIN 线程 使得计数减1... 唤醒所有子线程...
线程2_开始执行
线程1_开始执行
线程0_开始执行
线程3_开始执行
线程1_执行完毕
线程2_执行完毕
线程0_执行完毕
线程3_执行完毕