目录
CyclicBarrier【一组线程之间互相等待,计数器可以循环利用】
CountDownLatch【一个线程等待多个线程的场景】
用于协调一个线程等待一组线程的情况,内部类Sync基于AbstractQueuedSynchronizer的共享模式实现。构造器要求传入需要协调的个数(一般一个对应一个线程,即一次countDown方法调用),与Semaphore一样传入的数值会赋值为AQS的state字段,理解完上片的Semaphore实现限流,那么当前相当于设置一个值,多个线程执行完任务后调用countDown减去一,减完之后唤醒被阻塞的等待线程。一般我们是在Tomcat等线程池中的某个线程(当前的主线程),为了提高性能,使用了线程池,而当前线程需要等待多个任务的线程执行完任务后唤醒当前的Tomcat线程。模型大致如下图:
类似这样的场景(一个线程等待线程池任务的最后一个执行完,主线程处理各个维度的数据)之前分析过可以使用ThreadPoolExecutor#invokeAll方法、ExecutorCompletionService的take()#get()、CompletableFuture#of等情况(可见参见:并发编程工具 - CompletionService源码和项目使用、并发编程工具 - CompletableFuture的API和项目使用),这些场景都可以基于CountDownLatch实现,如:
/**
* {@link java.util.concurrent.CountDownLatch} 协调多线程与主线程的步调
* @author kevin
* @date 2020/8/1 0:03
* @since 1.0.0
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
List<Runnable> task = new ArrayList<>();
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
CountDownLatch countDownLatch = new CountDownLatch(3);
// 执行耗时任务, 如 数据库查询, io调用等
task.add(() -> {
queue.add(111);
countDownLatch.countDown();
});
task.add(() -> {
queue.add(222);
countDownLatch.countDown();
});
task.add(() -> {
queue.add(333);
countDownLatch.countDown();
});
OldSimpleThreadPool.executeRunnable(CREATE_ORDER, task.toArray(new Runnable[0]));
countDownLatch.await();
AtomicInteger total = new AtomicInteger();
queue.iterator().forEachRemaining(num -> {
total.addAndGet(num);
});
System.out.println(total.get());
}
}
CyclicBarrier【一组线程之间互相等待,计数器可以循环利用】
上面的CountDownLatch计数器一次使用完之后,下次还需要再使用时,则需要再new一个,有这样的场景就是一组线程等待再继续往下走(比如:生产消费模式、生产桌子需要配套凳子才能继续组合等)。在真实的项目中自己没有使用过,因为这样的生产消费模式很多都不是一个JVM里面,而是夸进程实现的场景。CyclicBarrier并没有像其他共享模式的AQS子类一样,使用自己的内部类,而是直接使用了ReentrantLock+Condition实现。
下面的demo,就是一定等到三个线程有数据到了再往下走,否则就需要等待,从打印的数据可以看出,打印完叠加的666,才可能执行下一次的打印每个线程,每两次打印666中间不会有空值【即不会连续打印两次666】。如下
/**
* 模拟三个耗时任务 循环执行, 最后的加任务就是 Barrier【屏障】
*
* 222
* 333
* 111
* 666
* -------------------
* 333
* 222
* 111
* 666
* -------------------
* 111
* 222
* 111
* 333
* 222
* 666
* -------------------
* 333
* 666
* -------------------
* 111
* 222
* 111
* 222
* 333
* 666
* -------------------
* 333
* 666
* -------------------
* 111
* 333
* 222
* 222
* 666
* -------------------
* 111
* 111
* 333
* 222
* 666
* -------------------
* 333
* 666
* -------------------
*
* @author kevin
* @date 2020/8/1 0:26
* @since 1.0.0
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
LinkedBlockingQueue<Integer> queue2 = new LinkedBlockingQueue<>();
LinkedBlockingQueue<Integer> queue3 = new LinkedBlockingQueue<>();
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
AtomicInteger total = new AtomicInteger();
try {
total.addAndGet(queue.take());
total.addAndGet(queue2.take());
total.addAndGet(queue3.take());
System.out.println(total.get());
System.out.println("-------------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
for (int i = 1; i < 10; i++) {
List<Runnable> task = new ArrayList<>();
// 执行耗时任务, 如 数据库查询, io调用等
task.add(() -> {
System.out.println(111);
queue.add(111);
try {
barrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
});
task.add(() -> {
System.out.println(222);
queue2.add(222);
try {
barrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
});
task.add(() -> {
System.out.println(333);
queue3.add(333);
try {
barrier.await();
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
});
OldSimpleThreadPool.executeRunnable(CREATE_ORDER, task.toArray(new Runnable[0]));
}
}
}