【发布时间】:2010-12-04 23:01:39
【问题描述】:
我在我的程序中使用 java 中的多线程。
我已经成功运行线程,但是当我使用Thread.wait() 时,它正在抛出java.lang.IllegalMonitorStateException。
如何让线程等到收到通知?
【问题讨论】:
-
Thread.wait() 不存在,可能是this.wait()
标签: java multithreading wait
我在我的程序中使用 java 中的多线程。
我已经成功运行线程,但是当我使用Thread.wait() 时,它正在抛出java.lang.IllegalMonitorStateException。
如何让线程等到收到通知?
【问题讨论】:
标签: java multithreading wait
您需要在synchronized 块中才能使Object.wait() 工作。
另外,我建议查看并发包,而不是老式的线程包。他们更安全,更方便easier to work with。
编辑
我假设您的意思是 Object.wait(),因为您的例外是当您尝试在不持有对象锁定的情况下获得访问权限时发生的情况。
【讨论】:
wait 是在 Object 中定义的,而不是在 Thread 中定义的。 Thread 上的监视器有点不可预测。
虽然所有 Java 对象都有监视器,但通常最好有一个专用锁:
private final Object lock = new Object();
通过使用命名类,您可以以较小的内存成本(每个进程大约 2K)获得更容易阅读诊断信息:
private static final class Lock { }
private final Object lock = new Lock();
为了wait 或notify/notifyAll 一个对象,您需要使用synchronized 语句来持有锁。此外,您将需要一个 while 循环来检查唤醒条件(查找有关线程的好文本来解释原因)。
synchronized (lock) {
while (!isWakeupNeeded()) {
lock.wait();
}
}
通知:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
在学习多线程时,了解 Java 语言和 java.util.concurrent.locks 锁(和 java.util.concurrent.atomic)是非常值得的。但尽可能使用java.util.concurrent 数据结构。
【讨论】:
wait 以外的任何方法,是的,您永远不会到达notify。但是,在Object.wait 的 API 文档中,“线程释放了此监视器的所有权”。因此,虽然在 wait 中,它就好像它在封闭的 synchronized 块之外(对于同一个对象,可能在同一个对象上有多个 synchronized 块)。
我知道这个帖子已经快 2 年了,但仍然需要关闭这个帖子,因为我也遇到了同样的问题...
请反复阅读此非法监视器异常的定义...
抛出 IllegalMonitorException 表示线程已尝试在对象的监视器上等待,或通知其他线程在对象的监视器上等待但不拥有指定的监视器。
这一行一再表示,IllegalMonitorException 在两种情况之一发生时出现......
1> 等待对象的监视器而不拥有指定的监视器。
2> 通知在对象的监视器上等待而不拥有指定监视器的其他线程。
有些人可能已经得到了答案……谁没有,那么请检查 2 个陈述……
同步(对象)
object.wait()
如果两个 object 相同...则不会出现非法MonitorException。
现在再次阅读 IllegalMonitorException 定义,您将不会再忘记它...
【讨论】:
根据您的 cmets,听起来您正在做这样的事情:
Thread thread = new Thread(new Runnable(){
public void run() { // do stuff }});
thread.start();
...
thread.wait();
存在三个问题。
正如其他人所说,只有当当前线程持有obj 的原始锁/互斥锁时,才能调用obj.wait()。如果当前线程没有持有锁,你会得到你所看到的异常。
thread.wait() 调用并没有按照您的预期进行。具体来说,thread.wait() 不会导致指定线程等待。相反,它会导致当前线程等到其他线程调用thread.notify() 或thread.notifyAll()。
实际上没有安全的方法来强制Thread 实例在它不想暂停时暂停。 (Java 与此最接近的是已弃用的 Thread.suspend() 方法,但该方法本质上是不安全的,如 Javadoc 中所述。)
如果您希望新启动的Thread 暂停,最好的方法是创建一个CountdownLatch 实例并让线程调用latch 上的await() 以暂停自身。然后主线程会在闩上调用countDown(),让暂停的线程继续。
与前面的点正交,使用Thread 对象作为锁/互斥锁可能会导致问题。例如,Thread::join 的 javadoc 说:
此实现使用以
this.isAlive为条件的this.wait调用循环。当一个线程终止时,this.notifyAll方法被调用。建议应用程序不要在Thread实例上使用wait、notify或notifyAll。
【讨论】:
由于您尚未发布代码,我们有点在黑暗中工作。异常的细节是什么?
您是从线程内部还是外部调用 Thread.wait()?
我问这个是因为根据 IllegalMonitorStateException 的 javadoc,它是:
抛出表明一个线程试图在一个对象的监视器上等待或通知其他线程在一个对象的监视器上等待而不拥有指定的监视器。
为了澄清这个答案,这个在线程上等待的调用也会抛出 IllegalMonitorStateException,尽管是从同步块中调用的:
private static final class Lock { }
private final Object lock = new Lock();
@Test
public void testRun() {
ThreadWorker worker = new ThreadWorker();
System.out.println ("Starting worker");
worker.start();
System.out.println ("Worker started - telling it to wait");
try {
synchronized (lock) {
worker.wait();
}
} catch (InterruptedException e1) {
String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
System.out.println (msg);
e1.printStackTrace();
System.out.flush();
}
System.out.println ("Worker done waiting, we're now waiting for it by joining");
try {
worker.join();
} catch (InterruptedException ex) { }
}
【讨论】:
wait() 的目标对象。
worker.wait() 行吗?那么你应该在worker上同步,而不是在锁上。
为了处理 IllegalMonitorStateException,您必须验证所有 wait、notify 和 notifyAll 方法的调用仅在调用线程拥有适当的监视器时发生。最简单的解决方案是将这些调用包含在同步块中。在同步语句中应该调用的同步对象是必须获取其监视器的对象。
这是理解监视器概念的简单示例
public class SimpleMonitorState {
public static void main(String args[]) throws InterruptedException {
SimpleMonitorState t = new SimpleMonitorState();
SimpleRunnable m = new SimpleRunnable(t);
Thread t1 = new Thread(m);
t1.start();
t.call();
}
public void call() throws InterruptedException {
synchronized (this) {
wait();
System.out.println("Single by Threads ");
}
}
}
class SimpleRunnable implements Runnable {
SimpleMonitorState t;
SimpleRunnable(SimpleMonitorState t) {
this.t = t;
}
@Override
public void run() {
try {
// Sleep
Thread.sleep(10000);
synchronized (this.t) {
this.t.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
【讨论】:
Thread.wait() 调用在与 Thread.class 对象同步的代码中有意义。我不认为这是你的意思。
你问
如何让线程等到收到通知?
您只能让当前线程等待。如果它同意,任何其他线程只能轻轻地要求等待。
如果您想等待某些条件,您需要一个锁定对象 - Thread.class 对象是一个非常糟糕的选择 - 它是一个单例 AFAIK,因此在其上进行同步(Thread 静态方法除外)是危险的。
Tom Hawtin 已经解释了同步和等待的细节。
java.lang.IllegalMonitorStateException 表示您正在尝试等待未同步的对象 - 这样做是非法的。
【讨论】:
不确定这是否会帮助其他人,但这是解决我在上面用户“Tom Hawtin - tacklin”的回答中的问题的关键部分:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
事实上,“lock”作为参数在 synchronized() 中被传递,它也被用于“lock”.notifyAll();
一旦我在这两个地方成功了,我就开始工作了
【讨论】:
我在尝试从不同的 class / 线程唤醒 / 中的线程时收到了 IllegalMonitorStateException。在java 8 中,您可以使用lock features of the new Concurrency API 代替 synchronized 函数。
我已经在WeakHashMap 中存储了asynchronous websocket 事务的对象。在我的情况下,解决方案也是store a lock object in a ConcurrentHashMap 回复synchronous。 注意condition.await(不是.wait)。
为了处理多线程,我使用了Executors.newCachedThreadPool() 创建了thread pool。
【讨论】:
使用Java 7.0或以下版本的可以参考我这里使用的代码。
public class WaitTest {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void waitHere(long waitTime) {
System.out.println("wait started...");
lock.lock();
try {
condition.await(waitTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
System.out.println("wait ends here...");
}
public static void main(String[] args) {
//Your Code
new WaitTest().waitHere(10);
//Your Code
}
}
【讨论】:
要在对象上调用 wait()/notify(),它需要在同步块内。所以首先你必须锁定对象然后才能调用这些函数。
synchronized(obj)
{
obj.wait()
}
详细解释: https://dzone.com/articles/multithreading-java-and-interviewspart-2
【讨论】: