【问题标题】:Unexpected deadlock in Executor执行程序中的意外死锁
【发布时间】:2014-03-29 04:45:57
【问题描述】:

我在 ThreadPoolExecutor 中运行任务时发现意外死锁。

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

  • 如果 corePoolSize >=2,则主要任务按预期完成。
  • 如果 corePoolSize
  • 改用 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();
       }
    }
    

【问题讨论】:

    标签: java concurrency threadpool deadlock executorservice


    【解决方案1】:

    你希望它如何处理线程数

    • 你只有 1 个执行线程
    • 首先尝试将辅助任务添加到执行程序队列并等待它开始

    当池中有空闲执行器时,执行器服务从队列中获取任务。在您的情况下(

    编辑:

    好的,我已经挖掘了一些信息,这就是我发现的。首先是来自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.
    

    好的,现在至于 queuess offer 方法

    同步队列:

    如果有另一个线程,则将指定元素插入此队列 等待接收。

    LinkedBlockingQueue

    将指定元素插入此队列,如有必要,等待空间可用。

    offer 方法的返回值决定了新任务是在新线程中排队还是在新线程中运行。

    LinkedBlockingQueue 将新任务加入队列,因为它可以,因为有足够的容量,任务被加入队列并且不会产生新线程。但是SyncQueu 不会将另一个任务加入队列,因为没有其他线程在等待加入队列(offer 返回 false,因为任务未加入队列),这就是为什么会产生新的执行线程。

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

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

    【讨论】:

    • 对不起,我听不懂。最多允许 4 个线程共存。还有 SyncQueue 案例...
    • 我希望最后的编辑能为你澄清一切
    • OK,次要任务入队了,我在帖子里已经说过了。问题是如果池中允许最多 4 个线程共存,为什么不启动它(请注意 ThreadPoolExecutor 构造函数)。
    • 我确实给你解释过了。因为如果队列能够接受另一个任务,它将被排队并且不会创建新线程。同步队列“不能”接受任务,这就是为什么产生新线程并且它对你有用。如果使用同步队列,则不会将任务添加到队列中。请仔细阅读我的编辑,因为它需要一些时间来编写,我不能在 cmets 中重复自己。解释就在那里,相信我
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-27
    相关资源
    最近更新 更多