Java 使用;通知()&&引用;等待();而不是",;暂停();及;resume();控制线程
我试图学习如何在java中暂停和恢复线程。我使用的是一个Java 使用;通知()&&引用;等待();而不是",;暂停();及;resume();控制线程,java,multithreading,applet,synchronization,synchronized,Java,Multithreading,Applet,Synchronization,Synchronized,我试图学习如何在java中暂停和恢复线程。我使用的是一个Applet,它实现了Runnable功能,有两个按钮“开始”和“停止” 线程的运行方法: public void run(){ while(true){ repaint(); try{ Thread.sleep(20); } catch(InterruptedException e) { e.printStackTrace(); } } } 现在,每当我尝试暂停或恢复线程时
Applet
,它实现了Runnable功能,有两个按钮“开始”和“停止”
线程的运行方法:
public void run(){
while(true){
repaint();
try{
Thread.sleep(20);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
现在,每当我尝试暂停或恢复线程时,都会引发异常:
线程“AWT-EventQueue-1”java.lang.IllegalMonitorStateException中的异常
注:
如果我使用不推荐使用的方法suspend()
和resume()
,那么前面的代码运行得很好,但是文档指出使用notify()
和wait()
来代替同步。我尝试将单词synchronized
添加到actionPerformed
方法中,但它仍然抛出异常
有人能解释一下为什么这不起作用,以及如何解决同步问题吗?很少的解释点真的会有很大帮助;) 我认为您必须在线程上同步,才能调用wait和notify。尝试使用
synchronized (th) {
th.notify();
}
与
wait()
一样,您不能只调用notify
和wait
。你得等点什么。在调用notify
之前,您必须确保不再需要等待
如果块尚未同步,则说明设计中存在错误
除非你有什么事要等,否则你怎么能叫wait
?如果你没有检查,你怎么知道还有什么要等呢?在不与控制该事件是否发生的代码同步的情况下,如何进行检查
除非发生了需要通知线程的事情,否则如何调用notify
?如果你没有持有可以告诉另一个线程的锁,那么怎么会发生另一个线程关心的事情呢
您应该像这样使用wait
:
while (something_to_wait_for()) wait();
而等待的东西应该检查受同步保护的东西。你不能让等待的东西同步,因为你有一个竞争条件——如果在等待的东西返回后,但在你进入等待之前发生了什么?那么你在等待已经发生的事情!因此,从根本上说,您需要同步。如果您只是在末尾添加它,那么您的设计就被破坏了
您的解决方案可能是添加一些等待的内容。也许您只需要一个简单的布尔变量。然后你的代码可以是while(should_wait)wait()代码>,应该等待=真
和应该等待=false();notifyAll()
。您需要同步以保护布尔值和等待
/通知
逻辑。您误解了等待()的工作原理。在线程
对象上调用等待
不会暂停该线程;相反,它告诉当前运行的线程等待其他事情发生。为了解释原因,我需要备份一点并解释synchronized
的实际功能
当您输入synchronized
块时,您将获得与对象关联的监视器。比如说,
synchronized(foo) {
获取与对象foo
关联的监视器
foo.wait();
拥有监视器后,在退出同步块之前,其他线程无法获取监视器。这是等待
和通知
进入的地方
wait
是对象类上的一个方法,它告诉当前运行的线程临时释放它所持有的监视器。这允许其他线程在foo
上同步
foo.wait();
除非有人在foo
上调用notify
或notifyAll
(或线程被中断),否则此线程不会继续。一旦发生这种情况,此线程将尝试重新获取foo
的监视器,然后继续。请注意,如果任何其他线程正在等待获取监视器,那么它们可能会首先进入——无法保证JVM将分发锁的顺序。请注意,如果没有人调用notify
或notifyAll
,则wait()
将永远等待。通常最好使用另一种需要超时的wait
。当有人调用notify
/notifyAll
或超时过期时,该版本将被唤醒
因此,您需要一个线程来进行等待,另一个线程来进行通知。wait
和notify
都必须将监视器保持在其试图等待或通知的对象上;这就是您看到非法监视器状态异常的原因
举个例子可以帮助您理解:
class RepaintScheduler implements Runnable {
private boolean paused = false;
private final Object LOCK = new Object();
public void run() {
while (true) {
synchronized(LOCK) {
if (paused) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
repaint();
}
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void pause() {
synchronized(LOCK) {
paused = true;
LOCK.notifyAll();
}
}
public void resume() {
synchronized(LOCK) {
paused = false;
LOCK.notifyAll();
}
}
}
然后,小程序代码可以执行以下操作:
public void init() {
RepaintScheduler scheduler = new RepaintScheduler();
// Add listeners that call scheduler.pause and scheduler.resume
btn_increment.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {
scheduler.resume();
}});
btn_decrement.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {
scheduler.pause();
}});
// Now start everything up
Thread t = new Thread(scheduler);
t.start();
}
请注意,Applet类不关心调度器如何暂停/恢复,也不具有任何同步块
因此,这里可能发生的一系列事件是:
- 线程A开始运行重绘计划程序
- 线程A进入睡眠状态20毫秒
- 线程B(事件调度线程)收到一个按钮点击;呼叫“暂停”
- 线程B在锁上获取监视器
- 线程B更新“暂停”变量并调用LOCK.notifyAll
- 没有线程在等待锁定,因此没有发生任何有趣的事情
- 线程B释放监视器on锁
- 线程A醒来,再次通过其循环
- 线程A在锁上获取监视器
- 线程A看到它应该暂停,所以它调用LOCK.wait
- 此时线程A挂起,等待有人调用notifyAll。线程A释放监视器on锁
- 一段时间后,用户单击“恢复”
- 线程B调用scheduler.resume
- 苏氨酸