java - 关于线程堕胎和死锁的问题 - volatile关键字

时间:2010-06-03 15:37:44

标签: java multithreading deadlock

我有一些麻烦要理解我必须停止正在运行的线程。我会试着通过例子来解释它。假设以下类:

public class MyThread extends Thread {
    protected volatile boolean running = true;

    public void run() {
        while (running) {
            synchronized (someObject) {
                while (someObject.someCondition() == false && running) {
                    try {
                        someObject.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // do something useful with someObject
            }
        }
    }

    public void halt() {
        running = false;
        interrupt();
    }
}

假设线程正在运行,并且以下语句被评估为true:

while (someObject.someCondition() == false && running)

然后,另一个线程调用MyThread.halt()。尽管此函数将'running'设置为false(这是一个易失性布尔值)并中断线程,但仍然执行以下语句:

someObject.wait();

我们陷入僵局。线程永远不会停止。

然后我想出了这个,但我不确定它是否正确:

public class MyThread extends Thread {
    protected volatile boolean running = true;

    public void run() {
        while (running) {
            synchronized (someObject) {
                while (someObject.someCondition() == false && running) {
                    try {
                        someObject.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // do something useful with someObject
            }
        }
    }

    public void halt() {
        running = false;
        synchronized(someObject) {
            interrupt();
        }
    }
}

这是对的吗?这是最常用的方法吗?

这似乎是一个显而易见的问题,但我没有提出解决方案。非常感谢你的帮助。

4 个答案:

答案 0 :(得分:2)

interrupt()调用将在被中断的线程中设置一个标志,someObject.wait()将always检查这个标志,所以你的第一个类应该工作。 AFAIC第一种是常见的方式,你的错误必须在其他地方。

答案 1 :(得分:1)

我试图模拟第一个版本,实际上,中断的标志会被记住。我不知道。这是我的代码如何模拟它:

public class Test {
    protected static class MyThread extends Thread {
        protected Object someObject = new Object();

        public void run() {
            for (int i = 0; i < Integer.MAX_VALUE; ++i) {
                /* this takes some time */
            }
            try {
                synchronized (someObject) {
                    someObject.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("WE COME HERE AFTER INTERRUPTED EXCEPTION");
            try {
                synchronized (someObject) {
                    someObject.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("HOWEVER, WE NEVER COME HERE, THE INTERRUPTED FLAG SEEMS TO BE RESETTED");
        }

        public void halt() {
            interrupt();
        }
    }

    public static void main(String[] a) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        t.halt();
    }
}

这让我感到疑惑。为什么

public void halt() {
    interrupt();
}

首选

public void halt() {
    synchronized(someObject) {
        someObject.notifyAll();
    }
}

在两个版本中,while(...)将再次评估?

答案 2 :(得分:0)

catch的{​​{1}}区块中,您应该包含:

InterruptedException

并将if (!running) break main; 循环标记为while (running)

无需在main: while (running)上进行同步即可拨打someObject

另外,我建议您重命名interrupt变量,因为它非常混乱。我建议running。因此:

shouldContinue

答案 3 :(得分:0)

或者....你可以使用重载等待方法,它将输入的最大毫秒数等待。现在,在您的halt方法中,您可以设置running=false并保证在指定的毫秒后等待调用将结束,同时将再次评估条件。

在下面的修改代码中,等待将阻止不超过1秒,然后再次检查while循环条件。这次运行设置为false,然后循环结束。

synchronized (someObject) {
    while (someObject.someCondition() == false && running) {
        try {
            someObject.wait(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
    }
}

注意:这种方法在高性能线程中可能不是最好的,但我发现它在很多情况下都很有用。

BTW - 如果JVM正在执行基本上非阻塞(不可中断)的逻辑,例如代码中的while循环条件,则然后由halt方法触发中断将'迷失'。而且您必须依赖'isInterrupted'标志来告诉逻辑是否调用了中断。