Executor中出现意外死锁

pla*_*ino 2 java concurrency deadlock executorservice threadpool

我在ThreadPoolExecutor中运行任务时发现了意外的死锁.

这个想法是一个主要任务,它启动一个改变标志的次要任务.主任务暂停,直到辅助任务更新标志.

  • 如果corePoolSize> = 2,则主任务按预期完成.
  • 如果corePoolSize <2,那么次要任务似乎已被强制但从未启动.
  • 相反,使用SynchronousQueue,即使对于corePoolSize = 0,主任务也会完成.

我想知道:

  • 造成僵局的原因是什么?从文档中可以看出这一点并不明显.
  • 为什么使用SynchronousQueue而不是LinkedBlockingQueue可以防止死锁?
  • corePoolSize = 2是一个安全值来防止这种死锁吗?

    import java.util.concurrent.*;
    class ExecutorDeadlock {
        /*------ FIELDS -------------*/
        boolean halted = true;
        ExecutorService executor;
        Runnable secondaryTask = new Runnable() {
            public void run() {
                System.out.println("secondaryTask started");
                halted = false;
                System.out.println("secondaryTask completed");
                }
        };
        Runnable primaryTask = new Runnable() {
            public void run() {
            System.out.println("primaryTask started");
            executor.execute(secondaryTask);
            while (halted) {
                try {
                    Thread.sleep(500);
                }
                catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            System.out.println("primaryTask completed");
          }
       };
    
       /*-------- EXECUTE -----------*/
       void execute(){
            executor.execute(primaryTask);
       }
    
       /*-------- CTOR -----------*/
       ExecutorDeadlock(int corePoolSize,BlockingQueue<Runnable> workQueue) {
            this.executor = new ThreadPoolExecutor(corePoolSize, 4,0L, TimeUnit.MILLISECONDS, workQueue);
       }
    
       /*-------- TEST -----------*/
       public static void main(String[] args) {
            new ExecutorDeadlock(2,new LinkedBlockingQueue<>()).execute();
            //new ExecutorDeadlock(1,new LinkedBlockingQueue<>()).execute();
           //new ExecutorDeadlock(0,new SynchronousQueue<>()).execute();
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)

Ant*_*sss 6

你如何期望这个线程计数<2如果

  • 你只有一个执行程序线程
  • 首先尝试将辅助任务添加到执行程序队列,然后使用WAITS启动它

当池中有可用执行程序时,执行程序服务从队列中获取任务.在你的情况下(<2)执行程序线程永远不会被第一个任务释放.这里没有死锁问题.

编辑:

好的,我挖出了一些信息,这就是我发现的.首先来自的一些信息ThreadPoolExecutor

任何BlockingQueue都可用于传输和保存提交的任务.此队列的使用与池大小调整交互:

If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
Run Code Online (Sandbox Code Playgroud)

好的,现在和queuess offer方法一样

SyncQueue:

如果另一个线程正在等待接收它,则将指定的元素插入此队列.

的LinkedBlockingQueue

将指定的元素插入此队列,等待空间变为可用.

offer方法的返回值确定新任务将在新线程中排队或运行.

由于LinkedBlockingQueue它可以因为它具有容量而被排队,因此任务被排队并且不会产生新的线程.但是SyncQueu不会排队另一个任务,因为没有其他线程正在等待某些事情被排队(offer因为任务没有入队而返回false),这就是为什么会产生新的执行程序线程.

如果您阅读javadocs ThreadPoolExecutor LinkedBlockingQueueSynchronousQueue+检查execute方法的实现,您将得出相同的结论.

所以你错了,文档中有explenation :)