ThreadPoolExecutor中的死锁

时间:2009-09-08 10:12:22

标签: java concurrency

遇到ThreadPoolExecutor函数停留在execute(Runnable)函数中且所有ThreadPool个线程都在getTask func中等待的情况时,workQueue为空。

有人有什么想法吗?

使用ThreadPoolExecutorArrayBlockingQueue

创建corePoolSize == maximumPoolSize = 4

[编辑]更准确地说,线程在ThreadPoolExecutor.exec(Runnable command) func中被阻止。它有执行任务,但不执行。

[Edit2]执行程序在工作队列(ArrayBlockingQueue)内的某处被阻止。

[Edit3] callstack:

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)

同时workQueue为空(使用远程调试检查)

[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);
    }
  }
}

6 个答案:

答案 0 :(得分:7)

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

从链接:

  

该错误是由各种Parker :: park()中缺少内存障碍引起的   可能导致唤醒和挂起丢失的路径。 (注意   内置同步使用的PlatformEvent :: park不容易受到攻击   问题)。 -XX:+ UseMembar构成一个解决方法,因为   状态转换逻辑中的膜屏障隐藏了问题   帕克::。 (也就是说,使用-UseMembar没有任何问题   机制,但+ UseMembar隐藏了Parker::)的bug。这是第一天   在JDK 5.0中添加了java.util.concurrent引入的bug。   我开发了一个简单的C模式的失败,似乎更有可能   在现代AMD和Nehalem平台上体现,可能是因为更深层次   存储缓冲区需要更长时间才能耗尽。我提供了一个暂定的解决方案   到Doug Lea的Parker :: park这似乎消除了这个bug。生病   将此修复程序提供给运行时。 (我还会增加CR   额外的测试用例和更长的解释)。这可能是一个   适合后端的候选人。

链接:JVM Bug

可以使用变通方法,但最好只获取最新的Java副本。

答案 1 :(得分:2)

我看不到ThreadPoolExecutor的{​​{1}}代码中的任何锁定。唯一的变量是execute(Runnable)。您为workQueue提供了什么BlockingQueue

关于死锁的主题:

您可以通过检查Windows上的ThreadPoolExecutor或UNIX系统上的<ctrl><break>提供的完整线程转储来确认这是一个死锁。

获得该数据后,您可以检查线程。以下是Sun's article on examining thread dumps (suggested reading)的相关摘录:

  

对于挂起,死锁或冻结程序:如果您认为程序挂起,则生成堆栈跟踪并检查状态MW或CW中的线程。如果程序死锁,那么一些系统线程可能会显示为当前线程,因为JVM没有其他任何东西可以做。

更轻松的说明:如果您在IDE中运行,可以确保在这些方法中没有启用断点。

答案 2 :(得分:1)

这种死锁可能是因为你从执行者本身运行任务。例如,您提交了一个任务,而这个任务会激活另外4个任务。如果你的池大小等于4,那么你只是完全溢出它,最后一个任务将等到任务返回值。但是第一个任务等待所有分叉任务完成。

答案 3 :(得分:0)

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

executor.shutdown()

让它终止,通常后跟executor.awaitTermination

答案 4 :(得分: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();
            }
        }
    }

}

答案 5 :(得分:0)

绝对奇怪。

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

  • 另一个BlockingQueue impl。,例如LinkedBlockingQueue

  • 在ArrayBlockingQueue中指定fairness = true,即使用new ArrayBlockingQueue(n, true)

从这两个选项中我会选择第二个'因为offer()被阻挡是非常奇怪的;我想到的一个原因 - Linux上的线程调度策略。就像一个假设。