1

我正在尝试实现一些基本的启动、停止、暂停和恢复功能,这些功能允许我进行以下状态转换:

  • 停止运行
  • 跑到停
  • 跑到暂停
  • 暂停运行
  • 暂停到停止(导致死锁)

大部分都按预期工作,但最后的状态转换是不可能的,因为它使线程冻结。有人可以解释一下为什么会发生这种情况以及如何预防吗?以下是代码的相关部分:

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

}
4

3 回答 3

3

wait();在 run 方法将永远等待,因为这些不是notify(); 当调用 stop 时,线程正在运行,因为永远等待,所以thread.join()会锁定。您需要在停止或更改等待时调用通知以wait(1000);

于 2013-05-01T14:49:56.280 回答
1
  1. stop方法中,thread.notify();之后 running = false;调用。(这将通知等待线程)。

  2. 然后你必须paused = false;在你的通知调用之前设置。

  3. 从您的方法中删除if (paused)块。run

  4. while (paused)将循环更改为while (paused && running). 或者,您可以while (paused) { wait(); if (!running) break;}根据您想要的控制流来使用。

  5. 为了更好地衡量,将volatile关键字添加到pausedrunning变量声明中(以创建跨线程的内存栅栏)。

于 2013-05-02T03:54:55.547 回答
1

让我们看看这里到底发生了什么:让我们将这里涉及的线程命名为 T2(您在代码中显式实例化和启动的线程)和 T1(调用 T2 线程Object上的 start、stop 方法)。T1 可能是您的主线程,具体取决于您其他未显示的代码。

由于以下事件序列,您会遇到死锁:(注意1:这只是一个可能的序列,此代码中可能还有其他可能的序列也可能导致死锁)

假设我们对 ThreadTest 对象执行 start()、pause() 和 stop(),如下所示(例如在 main() 中):

ThreadTest t = new ThreadTest();
t.start();
t.pause();
t.stop();
  1. 在 T1 中执行 pause() 后,T2 通过在“if(paused)”条件中输入“synchronized(this)”块来获取 ThreadTest 对象的锁。(注2:这里的“this”不是指T2线程对象,而是指ThreadTest对象,因为run()是ThreadTest类的一个方法。)

  2. T2 进入 wait() 并在进入 wait() 调用时释放 ThreadTest 对象锁(隐式)。

  3. 当 T1 进入 stop() 时,它会在 ThreadTest 对象上获得一个锁,因为 stop() 是一个同步方法。在 stop() 内部,T1 调用 t2.join(),并等待 T2 完成。但是 T2 已经处于 wait() 状态并且没有人唤醒它!

因此陷入僵局!

注意3:即使我们通过在 wait() 调用中指定超时或通过调用 notify() (如其他人所建议的那样)来唤醒 T2,它仍然无法退出等待,因为它无法重新获取(隐式)锁(在 ThreadTest 对象上),因为 T1 已经在 join() 中等待!

一种可能的解决方案: 虽然可能有很多可能的解决方案,但你可以试试这个吗?

在 stop() 方法中,而不是

thread.join();

你能用:

if (!paused) {
  thread.join();
} else {
  thread.interrupt();
}
于 2013-05-02T04:56:12.927 回答