11

我在 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,并且程序中没有关于线程的其他代码(所以我基​​本上只使用库中的代码)

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

4

2 回答 2

2

这个问题很容易重现,如果我们将你的代码包装在一些里面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)

这是什么意思?

如果不查看完整的项目代码(当然,我们不是在问它),很难百分百保证到底发生了什么。但是有两种可能:

1)您的restartMachine班级已停止运行此应用程序的机器。这导致JVM停止这样的续集

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

因此,您必须分析这些方式并了解与您的情况更相似的方式。

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

于 2015-04-03T13:52:31.227 回答
2

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

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]

即executor已经释放,但没有重新获取,mainLockin 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 时才出现,并且影响相当小(抛出了错误的异常类型),因此他们可能无法修复它。

于 2015-04-03T13:52:34.243 回答