安全的方法来阻止Java线程

时间:2014-05-21 16:23:16

标签: java multithreading

任何人都可以向我解释为什么第一个线程不起作用而第二个线程完美无缺:

public class Test {

    public static void main(String args[]) throws InterruptedException {
        TestThread1 t1 = new TestThread1();
        TestThread2 t2 = new TestThread2();

        t1.startThread();
        t2.start();

        Thread.sleep(4000);

        t1.stopThread();
        t2.stopThread();
    }
}

class TestThread1 extends Thread {

    private volatile TestThread1 thread;

    public void startThread() {
        thread = new TestThread1();
        thread.start();
    }

    public void run() {
        while (thread != null) {
            System.out.println("RUNNING 1 ...");
        }
    }

    public void stopThread() {
        thread = null;
    }
}

class TestThread2 extends Thread {

    private volatile boolean finished = false;

    public void run() {
        while (!finished) {
            System.out.println("RUNNING 2 ...");
        }
    }

    public void stopThread() {
        finished = true;
    }
}

当我在TestThread1内部进行调试时:在startThread内,thread成员已填充(因此不是null),run内{} {1}}成员是thread !!!最后,在null内,stopThread成员不是thread !!!

任何人都可以向我解释这里发生了什么吗?

5 个答案:

答案 0 :(得分:4)

在这里,您有两个TestThread1 t1实例:

  • 其中一个存储在t1本地变量中(使用main方法)。
  • 一个存储在您的thread实例变量(t1)中。

t1永远不会开始,t1.thread是。

t1.stopThread()t1.thread设置为null,但不会影响t1.thread.thread

由于您正在t1.thread开始,其run方法正在使用t1.thread.thread

  • 这永远不会设置为任何内容(因此它使用null)。
  • 像您一样调用t1.stopThread()只会将t1.thread设置为null,这不会影响t1.thread.thread

更一般地说,你不能只是“杀死”一个线程,但你可以在方法中实现测试,告诉它在某些情况下返回。你在第二次测试中所做的更接近于此(使用带有volatile变量的while (!finished) { ... })。

我不会将测试限制为finished。测试线程是否被中断也很有用,特别是因为如果在ExecutorService shutdownNow()内运行runnables会尝试中断它们(请参阅this question)。

我会使用while (!finished && !Thread.currentThread().isInterrupted()) { ... }

(请注意Thread.currentThread().isInterrupted()Thread.interrupted()之间的区别:它们看似相似,但后者也会重置您可能不想要的状态。)

根据循环中的内容(或者是否存在循环),您可能希望在不同的战略点使用if (finished || Thread.currentThread().isInterrupted()) { return; }之类的内容,这是有意义的。

答案 1 :(得分:0)

TestThread1 t1 = new TestThread1();
t1.startThread();

这只会在对象t1上调用方法startThread()。在此方法中,您将创建一个新线程。

thread = new TestThread1();
        thread.start();

但是对于这个Thread thread实例变量是null(对于t1它不是null)。

因此,在这两种情况下,线程变量都应为null。

答案 2 :(得分:0)

创建了两个TestThread1对象,一个启动,另一个停止。

我建议不要扩展Thread,而是将Runnable包装一次。

答案 3 :(得分:0)

因为在main方法中创建了Thread1对象。然后运行startThread,在第一个内创建一个不同的Thread1对象,并将其设置为字段线程。然后,您启动第二个没有初始化自己的线程字段的对象。当在第二个对象上运行run方法时,条件为false,而while循环不会启动。

您的对象层次结构看起来像这样

t1 (Thread1) {
    thread(Thread1): {
        thread: null;
        run() {
             while (thread != null) {...} // this is the method that is run - thread is null here since you never initialized it
        }
   };
   startThread() {} // calls the run method on the nested thread object above
   run() {
        while (thread != null) {...} // this method is not run since t1.start() is never called in main()
   }
}

答案 4 :(得分:0)

在你的情况下,

 t2.start();

直接调用run方法,而不是在thread方法中创建startThread

所以t1.stopThread()使volatile thread内的Thread1类为null。所以你就是这样。

溶液

使用

 t1.startThread();
        t2.startThread();

而不是

 t1.startThread();
        t2.start();

使2个线程在该方法中创建单独的线程。

如果你想要Thread1的一个线程,那么使用runnable接口并创建2个线程并分别调用startThread,而不是在main中创建额外的线程。