【问题标题】:Thread blocked on CountDownLatch await() when count is 0当计数为 0 时,线程在 CountDownLatch await() 上阻塞
【发布时间】:2012-05-30 05:42:10
【问题描述】:

在分析其中一个生产环境的日志时,我在倒计时等待()上看到一个处于“W​​AITING”状态的线程

...sun.misc.Unsafe.park(Native Method)
...java.util.concurrent.locks.LockSupport.park(Unknown Source)
...java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
...java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
...java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
...java.util.concurrent.CountDownLatch.await(Unknown Source)

锁存器被初始化为 1 并且另一个线程确实在锁存器的同一实例上调用了 countDown() 方法,但主线程仍然在锁存器上保持阻塞。这导致 jvm 被无限期挂起。

即使锁存器计数达到零也被阻止听起来不合理,我正在寻找有关进一步解决此问题的建议。

有什么想法吗?

注意 - 使用的 jvm 版本如下

java 版本“1.5.0_15” Java(TM) 2 运行时环境,标准版(内部版本 1.5.0_15-b04) Java HotSpot(TM) Client VM(build 1.5.0_15-b04,混合模式,共享)

更新 - 下面是我上面所说的线程的代码 sn-p

private class MyRunnable implements Runnable, Thread.UncaughtExceptionHandler {

        private AtomicBoolean shouldStop = new AtomicBoolean(false);
        private CountDownLatch stopLatch = new CountDownLatch(1);
        private Thread currentThread;

    public void run() {

        Thread.currentThread().setName("My Thread");
            Thread.currentThread().setUncaughtExceptionHandler(this);
            currentThread = Thread.currentThread();
            if (currentThread.isInterrupted()) {
                logger.debug("The pool thread had its interrupted stattus set. Clearing...");
                Thread.interrupted();
                logger.debug("The pool thread had its interrupted stattus set. Clearing...DONE");
            }
            try {
                doBusinessLogic(shouldStop);
            } catch (Exception e) {
                logger.error("An exception was encountered in the thread", e);
            } finally {
                if (currentThread.isInterrupted()) {
                    logger.debug("Clearing interupted status for the thread and returning to pool...");
                    Thread.interrupted();
                }
                stopLatch.countDown();
                logger.debug("Stopped task after counting down on the latch");
            }
        }

        public void stopThread() {
            shouldStop.set(true);
            logger.debug("Stop flag was set to true.. waiting for thread method to return...");
            try {
                stopLatch.await();
                logger.debug("Stop flag was set to true... task has finished. Returning.");
            } catch (InterruptedException e) {
                logger.error("Interrupted while awaiting thread stop event...", e);
            }
        }

        public void uncaughtException(Thread t, Throwable e) {
            logger.error("An uncaught exception occurred in the task thread ", e);
        }


        private void doBusinessLogic(AtomicBoolean shouldStop) {
    long sleepPeriod = 11;
    while (!shouldStop.get()) {
        try {
            Thread.sleep(sleepPeriod);
        } catch (InterruptedException e) {
            logger.debug("Thread was interrupted.Clearing interrupted status and proceeding", e);
            if (Thread.currentThread().isInterrupted())
                Thread.interrupted();
        }
        if (shouldStop.get()) {
            logger.debug("Stop flag was set. Returning.");
            return;
        }
        try {
            logger.debug("Performing business logic...");
            //.....
            logger.debug("Performing business logic...DONE");
        } catch (Throwable e) {
            logger.error("An exception occurred", e);
        }
        if (shouldStop.get()) {
            logger.debug("Stop flag was set. Returning.");
            return;
        }
    }

}


}

这是我在日志中看到的内容

DEBUG [main Thread] - Stop flag was set to true.. waiting for thread method to return...
DEBUG [My Thread]   - Stop flag was set. Returning.
DEBUG [My Thread]   - Stopped task after counting down on the latch

latch.await() 之后的 logger 语句从不打印,并且线程转储也表明主线程在 latch 上被阻塞。

【问题讨论】:

  • countDown() 在同一个实例上被调用 - 但我更倾向于相信你的代码和诊断中存在错误而不是 JRE图书馆。你能制作一个简短但完整的程序来演示这个问题吗?
  • 如果我能证明它,我可能知道根本原因:)。我之所以说调用 countDown() 是因为记录器语句表明了这一点。在调用 countDown() 之后添加了一些日志语句,这些语句可以在日志中看到。虽然线程转储表明主线程仍在等待锁存计数为零。
  • 我敢说countDown 被调用了——但是您的日志记录是否无可争辩地表明它是在同一个实例上调用的?
  • 添加了代码 sn-p 以获得清晰的概念。让我知道你的建议。
  • 你只有这个类的一个实例吗?每个实例都有自己的锁存器,所以如果你最终在某个地方创建了一个额外的实例并意外使用了它,那就可以解释了。

标签: java countdownlatch


【解决方案1】:

我本来想写这篇评论,但我的名声还不允许评论,所以我将在这里提供我的小经验。

我遇到了同样的问题。我的 countDownLatch 是从同步方法访问的:删除同步解决了问题(当然我不得不调整方法的代码以应对同步的缺失)。我不知道如何解释这种行为。

【讨论】:

  • 这叫死锁
  • 不,它不是:就像在原始问题中描述的那样,锁存器计数达到 0,所以没有任何东西阻塞等待线程。尽管如此,它还是没有醒来。 (据我记忆,那是 6 年前的事了……)
  • 我正在对您的“我的 countDownLatch 正在从同步方法访问:删除同步解决了问题(当然我不得不调整方法的代码以应对缺少同步)作出反应)作出反应。我不知道如何解释这种行为。这被称为死锁,因为进入同步块的人正在等待其他人进入同步块,但直到该线程离开时才能进入......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-27
  • 2015-11-08
  • 1970-01-01
  • 1970-01-01
  • 2017-10-06
相关资源
最近更新 更多