Java线程导致死锁

时间:2013-05-01 14:43:34

标签: java multithreading concurrency locking

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

  • 停止运行
  • 跑步停止
  • 正在暂停
  • 暂停运行
  • 暂停停止(造成死锁)

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

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

}

3 个答案:

答案 0 :(得分:3)

run方法中的

wait();将永远等待,因为这些不是notify(); 当调用停止时,线程正在运行,因为等待永远,因此thread.join()将锁定。 您需要在停止时呼叫通知或更改等待永久到wait(1000);

答案 1 :(得分: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变量声明中(以创建跨线程的内存栅栏)。

答案 2 :(得分:1)

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

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

假设我们在ThreadTest对象上执行start(),pause()然后停止(),如下所示(例如在main()中):

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

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

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

  4. 因此死锁!

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

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

    在stop()方法中,而不是

    thread.join();
    
    你可以使用:

    if (!paused) {
      thread.join();
    } else {
      thread.interrupt();
    }