我在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,并且没有关于程序中的线程的其他代码(所以我基本上只使用代码来自图书馆)
我很感激任何帮助,我知道有很多问题已经回答过这个问题(这个例外),但我不知道这里有什么问题。
答案 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时显现,并且影响相当小(抛出了错误的异常类型),因此它们可能无法修复它。