简体   繁体   English

awaitTermination函数的IllegalMonitorStateException

[英]IllegalMonitorStateException on awaitTermination function

I'm having a problem with using threads in Java (I have little experience with threads in Java, but much in C++, so i understand basic concept of threads). 我在Java中使用线程时遇到问题(我对Java中的线程经验不多,但在C ++中很多,所以我理解线程的基本概念)。 I've used example code for threads in Java, and the code is next: 我在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() is restarting some remote machines, and machines are not connected in any way, data that are being passed to Runnable are IP address for given machine, and command that are executing then locally on that machine. restartMachine()正在重新启动某些远程计算机,并且计算机没有以任何方式连接,正在传递给Runnable的数据是给定计算机的IP地址,以及在该计算机上本地执行的命令。

Error that I'm getting on execution of this piece of code is next: 接下来我正在执行这段代码时遇到错误:

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) 

Exception is thrown on calling of function awaitTermination() from code above. 从上面的代码调用函数awaitTermination()时抛出异常。 As I understand, and from various examples that I've seen there shouldn't be any problems with this code. 据我所知,从我见过的各种例子来看,这段代码不应该有任何问题。

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

Trace indicate that error is in calling of function mainLock.unlock(); 跟踪指示错误是在调用函数mainLock.unlock(); but as I understand it only main thread is going to execute that line, so I don't know why am I getting IllegalMonitorStateException, and there is no other code regarding threads in program (so I'm basically only using code from library) 但据我所知,只有主线程将执行该行,所以我不知道为什么我得到IllegalMonitorStateException,并且没有关于程序中的线程的其他代码(所以我基​​本上只使用来自库的代码)

I would appreciate any help, I know that there are many questions already answered regarding this problem (this exception), but I don't know what is the problem here. 我很感激任何帮助,我知道有很多问题已经回答了这个问题(这个例外),但我不知道这里有什么问题。

This problem could be easily reproduced, if we will wrap your code inside some Thread and then call on him deprecated (just for demonstrating problem) method stop , eg: 这个问题可以很容易地重现,如果我们将你的代码包装在一些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();
    }

Running this code, we always get "desired" exception: 运行此代码,我们总是得到“期望的”异常:

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)

What does it mean? 这是什么意思?

Without viewing full project code (of course, we are not asking it), hard to say with 100% waranty what did happened. 如果没有查看完整的项目代码(当然,我们不是要求它),很难说100%保证发生了什么。 But there are 2 possibilities: 但有两种可能性:

1) Your restartMachine class has stopped machine on which this application was running itself. 1)您的restartMachine类已停止此应用程序自行运行的计算机。 This caused to JVM stopping with such sequel 这导致JVM停止了这样的续集

2) Some where in your application you run mentioned code in other thread, which somewhere was stopped in way which I have described or another one. 2)在你的应用程序中你运行的某些地方提到了其他线程中的代码,这个代码在我所描述的方式或另一个方面被停止了。

So, you have to analyze these ways and understand what could be more similiar to your situation. 因此,您必须分析这些方法并了解可能更类似于您的情况。

UPD : Just another idea, 3) if you are running your application under Tomcat for example, this also can lead to such problem when Tomcat stops your application. UPD :另一个想法,3)如果你在Tomcat下运行你的应用程序,当Tomcat停止你的应用程序时,这也会导致这样的问题。

This is very peculiar, and probably not your fault: 这非常奇特,可能不是你的错:

The Javadoc of ReentrantLock.unlock says: ReentrantLock.unlock的Javadoc说:

throws IllegalMonitorStateException if the current thread does not hold this lock 如果当前线程没有持有此锁,则抛出IllegalMonitorStateException

but the implementation of awaitTermination you have posted shows that the thread has successfully locked the very same object (through the final variable mainLock ) previously. 但是你发布的awaitTermination的实现表明该线程之前已成功锁定了同一个对象(通过最终变量mainLock )。 Therefore, there has been an intermediary unlock, or the ReentrantLock implementation has a bug (in its Java code, or native code, or possibly even the hard). 因此,有一个中间解锁,或者ReentrantLock实现有一个错误(在其Java代码中,或本机代码,甚至可能是硬件)。 Further analysis is necessary to discover which is the case. 需要进一步分析以发现情况。 As you are currently the only one to be able to reproduce the problem, you are the only one that can perform that analysis effectively. 由于您目前是唯一能够重现问题的人,因此您是唯一能够有效执行该分析的人。

A reasonable first step would be to launch the application in debug mode, and set a breakpoint in AbstractOwnableSynchronizer.setExclusiveOwnerThread to verify whether there has been an intermediary unlock (and if so, from where). 合理的第一步是在调试模式下启动应用程序,并在AbstractOwnableSynchronizer.setExclusiveOwnerThread设置断点以验证是否存在中间解锁(如果是,则从哪里开始)。 Should the presence of the breakpoint cause the problem to disappear (because it is timing sensitive), you might use a conditional breakpoint that never halts, but whose condition logs to System.out for your inspection, instead. 如果断点的存在导致问题消失(因为它是时序敏感的),您可能会使用一个永不停止的条件断点,但其条件会记录到System.out以供检查。

Update Thanks to the reproducer provided by Andremoniy in his answer, I was able to perform this analysis myself. 更新感谢Andremoniy在他的回答中提供的复制器,我能够自己执行此分析。 I used the following expression in the conditional breakpoint to obtain the stack trace whenever the lock is aquired or released: 我在条件断点中使用以下表达式,以便在获取或释放锁时获取堆栈跟踪:

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

Here is relevant part of the log output for his code: 以下是他的代码的日志输出的相关部分:

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]

That is, the executor has released, but not reacquired, mainLock in awaitNanos , which is implemented as follows: 也就是说,执行器已经发布,但不是重新获得, mainLockawaitNanos ,其执行如下:

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

As we can see from the absence of a finally block, the method is not exception safe, ie the lock not reacquired when an exception is thrown (such as the ThreadDeathException caused by Thread.stop() ). 从缺少finally块可以看出,该方法不是异常安全的,即抛出异常时不会重新获取锁(例如Thread.stop()引起的ThreadDeathException )。

You might wish to report this bug to Oracle. 您可能希望向Oracle报告此错误。 However, since it only appears to manifest upon use of a deprecated api, and the impact is rather minor (wrong exception type is thrown), they might not fix it. 但是,由于它似乎只在使用不推荐的api时出现,并且影响相当小(抛出了错误的异常类型),因此它们可能无法修复它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM