我正在尝试实现一些基本的启动,停止,暂停和恢复功能,允许我进行以下状态转换:
大多数都按预期工作,但最后一次状态转换是不可能的,因为它会使线程冻结。有人可以解释一下为什么会发生这种情况以及如何预防吗?以下是代码的相关部分:
public class ThreadTest implements Runnable {
private volatile boolean running = false;
private volatile boolean paused = false;
private Thread thread;
public ThreadTest() {
thread = new Thread(this);
}
public void run() {
while (running) {
try {
if (paused) {
synchronized (this) {
while (paused)
wait();
}
}
}
catch (InterruptedException e) {
}
}
}
public synchronized void start() {
if(running && !thread.isAlive())
return;
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop() {
if(!running && thread.isAlive())
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
public synchronized void resume() {
if(paused) {
paused = false;
notify();
}
else {
return;
}
}
public synchronized void pause() {
if(!paused) {
paused = true;
}
else {
return;
}
}
}
答案 0 :(得分:3)
wait();
将永远等待,因为这些不是notify();
当调用停止时,线程正在运行,因为等待永远,因此thread.join()
将锁定。
您需要在停止时呼叫通知或更改等待永久到wait(1000);
答案 1 :(得分:1)
在stop
方法中,在 thread.notify();
后调用running = false;
。 (这将通知等待线程。)
然后,必须在通知电话之前设置paused = false;
。
从if (paused)
方法移除run
块。
将while (paused)
循环更改为while (paused && running)
。或者,您可以使用while (paused) { wait(); if (!running) break;}
,具体取决于您想要的控制流程。
为了更好地衡量,请将volatile
关键字添加到paused
和running
变量声明中(以创建跨线程的内存栅栏)。
答案 2 :(得分:1)
让我们看看这里到底发生了什么: 让我们将这里涉及的线程命名为T2(您在代码中显式实例化并启动的线程)和T1(调用T2线程 Object 上的start,stop方法)。 T1可能是你的主线程,取决于你未提出的其他代码。
由于以下一系列事件,您将遇到死锁: ( Note1 :这只是一个可能的序列,此代码中可能还有其他可能的序列,也可能导致死锁)
假设我们在ThreadTest对象上执行start(),pause()然后停止(),如下所示(例如在main()中):
ThreadTest t = new ThreadTest();
t.start();
t.pause();
t.stop();
在T1中执行pause()后,T2通过在“if(paused)”条件内输入“synchronized(this)”块来获取对ThreadTest对象的锁定。 ( Note2 :这里的“this”不是指T2线程对象,而是指ThreadTest对象,因为run()是ThreadTest类的一个方法。)
T2进入wait()并在进入wait()调用时释放ThreadTest对象锁(隐式)。
当T1进入stop()时,它获取ThreadTest对象的锁定,因为stop()是同步方法。在stop()内,T1调用t2.join(),等待T2完成。 但是T2已经处于等待状态(并且没有人将其唤醒!)
因此死锁!
Note3 :即使我们通过在wait()调用中指定超时或通过调用notify()(如其他人的建议)来唤醒T2,它仍然无法退出等待,因为它无法重新获取(隐式)锁(在ThreadTest对象上),因为T1已经在join()中等待了!
一种可能的解决方案: 虽然可能有很多可能的解决方案,你可以试试这个吗?
在stop()方法中,而不是
thread.join();
你可以使用:
if (!paused) {
thread.join();
} else {
thread.interrupt();
}