2009-09-08 133 views
13

遇到ThreadPoolExecutor停在execute(Runnable)函数中的情况,而所有ThreadPool线程正在等待getTask func,workQueue为空。ThreadPoolExecutor中的死锁

有没有人有任何想法?

ThreadPoolExecutor的与ArrayBlockingQueue创建,并且corePoolSize == maximumPoolSize = 4

[编辑]为了更加精确,线程被阻塞在ThreadPoolExecutor.exec(Runnable command) FUNC。它有执行的任务,但不这样做。

[编辑2]执行程序被阻塞在工作队列中的某处(ArrayBlockingQueue)。

[EDIT3]调用堆栈:

thread = front_end(224) 
at sun.misc.Unsafe.park(Native methord) 
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:158) 
at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:747) 
at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:778) 
at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1114) 
at 
java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) 
at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) 
at java.util.concurrent.ArrayBlockingQueue.offer(ArrayBlockingQueue.java:224) 
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:653) 
at net.listenThread.WorkersPool.execute(WorkersPool.java:45) 

在同一时间工作队列是空的(选中使用远程调试)

[Edit4]代码与ThreadPoolExecutor工作:

public WorkersPool(int size) { 
    pool = new ThreadPoolExecutor(size, size, IDLE_WORKER_THREAD_TIMEOUT, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(WORK_QUEUE_CAPACITY), 
     new ThreadFactory() { 
     @NotNull 
     private final AtomicInteger threadsCount = new AtomicInteger(0); 

     @NotNull 
     public Thread newThread(@NotNull Runnable r) { 
      final Thread thread = new Thread(r); 
      thread.setName("net_worker_" + threadsCount.incrementAndGet()); 
      return thread; 
     } 
     }, 

     new RejectedExecutionHandler() { 
     public void rejectedExecution(@Nullable Runnable r, @Nullable ThreadPoolExecutor executor) { 
      Verify.warning("new task " + r + " is discarded"); 
     } 
     }); 
    } 

    public void execute(@NotNull Runnable task) { 
    pool.execute(task); 
    } 

    public void stopWorkers() throws WorkersTerminationFailedException { 
    pool.shutdownNow(); 
    try { 
     pool.awaitTermination(THREAD_TERMINATION_WAIT_TIME, TimeUnit.SECONDS); 
    } catch (InterruptedException e) { 
     throw new WorkersTerminationFailedException("Workers-pool termination failed", e); 
    } 
    } 
} 
+0

什么任务的性质传递给TPE.execute()FUNC。?如果任务可以访问TPE,那么这可能是您的问题。 – artemv 2010-03-12 12:09:50

+1

我觉得我在1.7.0_13上有类似的问题。这个过程开始并且没有问题......然后在某些时候我有200个任务,但是我的阻塞队列是空的。核心池的大小是3 ...我也使用ArrayBlockingQueue .... – cljk 2013-09-19 12:45:32

回答

2

我没有看到ThreadPoolExecutor的代码execute(Runnable)中的任何锁定。唯一的变量是workQueue。你提供了什么样的BlockingQueue给你的ThreadPoolExecutor

有关死锁的话题:

您可以确认这是通过检查全部线程转储,因为在UNIX系统上的Windows或kill -QUIT提供由<ctrl><break>死锁。

一旦你有了这些数据,你就可以检查这些线程。下面是一个Sun's article on examining thread dumps (suggested reading)相关摘录:

挂,僵持或冻结的程序:如果你认为你的程序是挂,产生一个堆栈跟踪并检查状态兆瓦或CW线程。如果程序死锁,那么一些系统线程可能会显示为当前线程,因为JVM没有其他任何东西可以执行。

在一个简单的说明中:如果您正在IDE中运行,您是否可以确保在这些方法中没有启用断点。

+0

正如我在我的问题中写的,使用了ArrayBlockingQueue。它是空的。是的,线程在工作队列中的某个地方阻塞。 – Vitaly 2009-09-09 06:04:57

+0

我用远程调试。 编辑了这个问题 - 添加了callstack。 – Vitaly 2009-09-09 06:12:26

+0

你也可以使用JConsole – pjp 2009-09-09 08:37:10

0

正如已经提到的人,这听起来像正常行为,ThreadPoolExecutor只是在等待一些工作。如果你想停止它,你需要调用:

executor.shutdown()

让它终止,通常后跟一个执行者。awaitTermination

+0

编辑了这个问题。 – Vitaly 2009-09-09 06:05:41

0

库代码源代码如下(这实际上是从http://spymemcached.googlecode.com/files/memcached-2.4.2-sources.zip类),
- 有点复杂 - 对FutureTask多次呼吁更多的保护,如果我没有记错的话 - 但似乎并没有像易死锁 - 非常简单的ThreadPool用法:

package net.spy.memcached.transcoders; 

import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.Future; 
import java.util.concurrent.FutureTask; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.TimeoutException; 
import java.util.concurrent.atomic.AtomicBoolean; 

import net.spy.memcached.CachedData; 
import net.spy.memcached.compat.SpyObject; 

/** 
* Asynchronous transcoder. 
*/ 
public class TranscodeService extends SpyObject { 

    private final ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 10, 60L, 
      TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100), 
      new ThreadPoolExecutor.DiscardPolicy()); 

    /** 
    * Perform a decode. 
    */ 
    public <T> Future<T> decode(final Transcoder<T> tc, 
      final CachedData cachedData) { 

     assert !pool.isShutdown() : "Pool has already shut down."; 

     TranscodeService.Task<T> task = new TranscodeService.Task<T>(
       new Callable<T>() { 
        public T call() { 
         return tc.decode(cachedData); 
        } 
       }); 

     if (tc.asyncDecode(cachedData)) { 
      this.pool.execute(task); 
     } 
     return task; 
    } 

    /** 
    * Shut down the pool. 
    */ 
    public void shutdown() { 
     pool.shutdown(); 
    } 

    /** 
    * Ask whether this service has been shut down. 
    */ 
    public boolean isShutdown() { 
     return pool.isShutdown(); 
    } 

    private static class Task<T> extends FutureTask<T> { 
     private final AtomicBoolean isRunning = new AtomicBoolean(false); 

     public Task(Callable<T> callable) { 
      super(callable); 
     } 

     @Override 
     public T get() throws InterruptedException, ExecutionException { 
      this.run(); 
      return super.get(); 
     } 

     @Override 
     public T get(long timeout, TimeUnit unit) throws InterruptedException, 
       ExecutionException, TimeoutException { 
      this.run(); 
      return super.get(timeout, unit); 
     } 

     @Override 
     public void run() { 
      if (this.isRunning.compareAndSet(false, true)) { 
       super.run(); 
      } 
     } 
    } 

} 
0

绝对奇怪。

但在此之前编写自己的TPE尝试:

  • 另一个BlockingQueue IMPL,例如LinkedBlockingQueue

  • 在ArrayBlockingQueue指定公平=真,即使用new ArrayBlockingQueue(n, true)

来自这两个OPTS我会选择第二个“的原因,而是很奇怪,offer()被阻止;记住一个原因 - Linux上的线程调度策略。就像一个假设一样。

7

这听起来像是一个JVM的年龄超过6u21的错误。在某些(可能是所有)操作系统的编译本机代码中存在一个问题。

从链接:

该错误是由缺少各种帕克记忆力障碍有:公园() 路径,可能导致丢失唤醒和挂起引起的。 (请注意 PlatformEvent :: park内置的同步使用不易受到 问题的影响)。 -XX:+ UseMembar定义了解决方法,因为状态转换逻辑中的 膜障屏蔽了 Parker ::中的问题。 (也就是说,使用-UseMembar 机制没有任何问题,但+ UseMembar隐藏了错误Parker::)。这是在JDK 5.0中添加java.util.concurrent引入的第一天的 错误。 我开发了一种简单的C模式的失败,它似乎更有可能在现代AMD和Nehalem平台上显示 ,可能是因为更深的存储缓冲区需要耗费更长的时间。我向Parker :: park的Doug Lea提供了一个试验性的修复 ,它似乎消除了这个错误。我将 提供此修复程序到运行时。 (我还会用 附加测试用例和更长的解释来增加CR。这可能是一个很好的备选端口。

链接:JVM Bug

解决方法是可用的,但你很可能是最好关闭刚开最新的Java的副本。

+1

我升级到'build 1.6.0_27-b07'(在Solaris 10 SPARC上运行),但仍不能解决问题。我的Jboss ESB仍然创建了数千个线程并且不关闭它们。 – 2011-10-15 07:12:13

1

这种死锁可能是因为你从执行器本身运行任务。例如,你提交了一个任务,而这个任务又发射了4个任务。如果池大小等于4,那么你完全溢出它,最后一个任务将等待任务返回值的某个人。但第一项任务等待所有分支任务完成。