awaitTermination函数的IllegalMonitorStateException

时间:2015-04-03 11:28:20

标签: java multithreading illegalmonitorstateexcep

我在Java中使用线程时遇到问题(我对Java中的线程很少有经验,但在C ++中很多,所以我理解线程的基本概念)。我在Java中使用了线程的示例代码,接下来是代码:

        ExecutorService executor = Executors.newFixedThreadPool(machines.size());

        for (Machine m : machines) {
            Runnable worker = new restartMachine(m.dataformachine());
            executor.execute(worker);
        }

        executor.shutdown();
        try {
            executor.awaitTermination(15, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

restartMachine()正在重新启动某些远程计算机,并且计算机没有以任何方式连接,传递给Runnable的数据是给定计算机的IP地址,以及在该计算机上本地执行的命令。

我接下来要执行这段代码时出错:

java.lang.IllegalMonitorStateException
 at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
 at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
 at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
 at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1471) 

从上面的代码调用函数awaitTermination()时会抛出异常。据我所知,从我所见过的各种例子来看,这段代码不应该有任何问题。

public boolean awaitTermination(long timeout, TimeUnit unit)
    throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (;;) {
            if (runStateAtLeast(ctl.get(), TERMINATED))
                return true;
            if (nanos <= 0)
                return false;
            nanos = termination.awaitNanos(nanos);
        }
    } finally {
        mainLock.unlock();
    }
}

跟踪指示错误是在调用函数mainLock.unlock(); 但据我所知,只有主线程将执行该行,所以我不知道为什么我得到IllegalMonitorStateException,并且没有关于程序中的线程的其他代码(所以我基​​本上只使用代码来自图书馆)

我很感激任何帮助,我知道有很多问题已经回答过这个问题(这个例外),但我不知道这里有什么问题。

2 个答案:

答案 0 :(得分:1)

如果我们将您的代码包含在某些Thread中,然后调用已弃用(仅用于演示问题)方法stop,则可以轻松复制此问题,例如:

  private void method() throws InterruptedException {
        Runnable runnable = new Runnable() {
            public void run() {
                ExecutorService executor = Executors.newFixedThreadPool(1);
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(10000L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });

                executor.shutdown();

                try {
                    executor.awaitTermination(3, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(1000L);
        thread.stop();
    }

运行此代码,我们总是得到“期望的”异常:

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
    at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
    at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1471)
    at q29431344.TestThreads$1.run(TestThreads.java:37)
    at java.lang.Thread.run(Thread.java:724)

这是什么意思?

如果没有查看完整的项目代码(当然,我们不是要求它),很难说100%保证发生了什么。但有两种可能性:

1)您的restartMachine类已停止此应用程序自行运行的计算机。这导致JVM停止了这样的续集

2)你运行的应用程序中的某些地方提到了其他线程中的代码,这些代码在某个地方以我所描述的方式或其他方式停止。

所以,你必须分析这些方法,并了解你的情况可能更类似的东西。

UPD :只是另一个想法,3)如果您在Tomcat下运行您的应用程序,当Tomcat停止您的应用程序时,这也可能导致此类问题。

答案 1 :(得分:0)

这非常奇特,可能不是你的错:

ReentrantLock.unlock的Javadoc说:

  如果当前线程没有持有此锁

,则

抛出IllegalMonitorStateException

但是您发布的awaitTermination的实现表明该线程先前已成功锁定了同一个对象(通过最终变量mainLock)。因此,有一个中间解锁,或者ReentrantLock实现有一个错误(在其Java代码中,或本机代码,甚至可能是硬件)。需要进一步分析以发现情况。由于您目前是唯一一个能够重现问题的人,因此您是唯一能够有效执行该分析的人。

合理的第一步是在调试模式下启动应用程序,并在AbstractOwnableSynchronizer.setExclusiveOwnerThread中设置断点以验证是否存在中间解锁(如果是,则从哪里开始)。如果断点的存在导致问题消失(因为它是时序敏感的),您可能会使用一个永不停止的条件断点,但其条件会记录到System.out以供检查。

<强>更新 感谢Andremoniy在他的回答中提供的复制器,我能够自己进行这种分析。我在条件断点中使用以下表达式,以便在获取或释放锁时获取堆栈跟踪:

new RuntimeException(this + " is now owned by " + arg0).printStackTrace();
return false;

以下是他的代码的日志输出的相关部分:

java.lang.RuntimeException: java.util.concurrent.locks.ReentrantLock$NonfairSync@a5e3519[State = 1, empty queue] is now owned by null
    at java.util.concurrent.locks.AbstractOwnableSynchronizer.setExclusiveOwnerThread(AbstractOwnableSynchronizer.java:74)
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2069)
    at java.util.concurrent.ThreadPoolExecutor.awaitTermination(ThreadPoolExecutor.java:1465)
    at stackoverflow.Test$1.run(Test.java:24)
    at java.lang.Thread.run(Thread.java:745)

...

java.util.concurrent.locks.ReentrantLock$NonfairSync@a5e3519[State = 0, empty queue] could not be released, as it is owned by null rather than Thread[Thread-0,5,main]

也就是说,执行者已在mainLock中释放awaitNanos但未重新获取,其实现方式如下:

    public final long awaitNanos(long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        final long deadline = System.nanoTime() + nanosTimeout;
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            if (nanosTimeout <= 0L) {
                transferAfterCancelledWait(node);
                break;
            }
            if (nanosTimeout >= spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
            nanosTimeout = deadline - System.nanoTime();
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
        return deadline - System.nanoTime();
    }

从缺少finally块可以看出,该方法不是异常安全的,即抛出异常时不会重新获取锁(例如由ThreadDeathException引起的Thread.stop())。

您可能希望向Oracle报告此错误。但是,由于它似乎仅在使用不推荐的api时显现,并且影响相当小(抛出了错误的异常类型),因此它们可能无法修复它。