2014-02-26 67 views
2

我在ThreadPoolExecutor中运行任务时发现了一个意外的死锁。Executor中的意外死锁

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

  • 如果corePoolSize> = 2,则主任务按预期完成。
  • 如果corePoolSize < 2似乎是次要任务是enquenqued但从未启动。
  • 改为使用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(); 
        } 
    } 
    

回答

7

你怎么能指望这个工作的线程数< 2如果

  • 你只有1个执行线程
  • 第一TAST添加次要任务的执行队列中,等待为它启动

任务是由执行程序服务从队列中提取时在游泳池里是免费的执行者。在你的情况下(< 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. 

好,现在作为用于queuess offer方法

SyncQueue:

将指定元素插入此队列中,如果另一线程是 等待接收它。

的LinkedBlockingQueue

将指定元素插入此队列中,如果有必要等待空间变得可用。 offer方法

返回值决定whetever新的任务将被排队或在新的线程运行。

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

如果您阅读javadocs为ThreadPoolExecutorLinkedBlockingQueueSynchronousQueue +检查execute方法的实现,您会得到相同的结论。

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

+0

你的意思是corePoolSize包括任务来管理队列? – plastilino

+0

它指定执行程序线程数 – Antoniossss

+0

对不起,我无法得到它。最多允许4个线程共存。并且还有SyncQueue情况... – plastilino