先上代码:
import java.util.concurrent.*; public class App { public static ExecutorService pool = Executors.newFixedThreadPool(5); public static ExecutorService pool2 = Executors.newCachedThreadPool(); public static void main(String[] args) { // 使用 CyclicBarrier 出现死锁问题 // new GenTaskUseBarrier(pool, 10).start(); // 使用 CachedThreadPool 解决 CyclicBarrier 死锁问题 // new GenTaskUseBarrier(pool2, 10).start(); // 使用 CountDownLatch 解决 CyclicBarrier 死锁问题 // new GenTaskUseCountDown(pool, 10).start(); // 模拟5个并发共享一个线程池 // new MockConcurrence(pool, 5).start(); } } class MockConcurrence extends Thread { ExecutorService pool; int count; public MockConcurrence(ExecutorService pool, int count) { super(); this.pool = pool; this.count = count; } @Override public void run() { for (int i = 0; i < count; i++) { new GenTaskUseBarrier(pool, 5, false).start(); } } } class GenTaskUseCountDown extends Thread { ExecutorService pool; int taskSize; public GenTaskUseCountDown(ExecutorService pool, int taskSize) { this.pool = pool; this.taskSize = taskSize; } @Override public void run() { CountDownLatch latch = new CountDownLatch(taskSize); for (int i = 0; i < taskSize; i++) { pool.submit(new MyTask(latch)); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } pool.shutdown(); System.out.println("All task done!"); } } class GenTaskUseBarrier extends Thread { ExecutorService pool; int taskSize; boolean autoClose = true; public GenTaskUseBarrier(ExecutorService pool, int taskSize) { this.pool = pool; this.taskSize = taskSize; } public GenTaskUseBarrier(ExecutorService pool, int taskSize, boolean autoClose) { this(pool, taskSize); this.autoClose = autoClose; } @Override public void run() { CyclicBarrier barrier = new CyclicBarrier(taskSize, new Runnable() { @Override public void run() { if (autoClose) { pool.shutdown(); } System.out.println("All task done!"); } }); for (int i = 0; i < taskSize; i++) pool.submit(new MyTask(barrier)); } } class MyTask extends Thread { CyclicBarrier barrier; CountDownLatch latch; public MyTask(CyclicBarrier barrier) { this.barrier = barrier; } public MyTask(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { System.out.println("线程" + Thread.currentThread().getName() + "正在执行同一个任务"); // 以睡眠来模拟几个线程执行一个任务的时间 Thread.sleep(1000); System.out.println("线程" + Thread.currentThread().getName() + "执行任务完成,等待其他线程执行完毕"); // 用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务; if (barrier != null) { barrier.await(); } if (latch != null) { latch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("所有线程写入完毕"); } }
输出结果:
线程pool-1-thread-3正在执行同一个任务 线程pool-1-thread-4正在执行同一个任务 线程pool-1-thread-2正在执行同一个任务 线程pool-1-thread-1正在执行同一个任务 线程pool-1-thread-5正在执行同一个任务 线程pool-1-thread-2执行任务完成,等待其他线程执行完毕 线程pool-1-thread-3执行任务完成,等待其他线程执行完毕 线程pool-1-thread-4执行任务完成,等待其他线程执行完毕 线程pool-1-thread-1执行任务完成,等待其他线程执行完毕 线程pool-1-thread-5执行任务完成,等待其他线程执行完毕 //卡死在这个地方
原因分析:
1.提交了10个任务,但是只有5个调度线程、每次只能执行一个解析任务;
2.前面5个任务执行后由于调用了 barrier.await 被阻塞了,需要等待其他两个任务都达到栅栏状态;
3.前面5个任务的线程被阻塞了,导致没有空闲的调度线程去执行另外两个任务;
4.前面5个任务等待其他两个任务的栅栏唤醒,而其他5个任务则等待第一个任务的线程资源,从而进入死锁状态。
解决方案 [修改Main方法注释 可以逐个测试]:
1、调整线程池调度线程个数,提交多少个任务开多少个资源,如果并发调用的时候共享同一个线程池调度非常容易出现问题,一定要小心。
2、使用 CachedThreadPool,或者自定义线程池
3、更换协作工具类为 CountDownLatch,将主线程阻塞直到所有的解析任务都被执行完成。