停止Java线程的安全方法

停止Java线程的安全方法,java,multithreading,Java,Multithreading,有谁能解释一下为什么第一个线程不工作,而第二个线程工作得很好: public class Test { public static void main(String args[]) throws InterruptedException { TestThread1 t1 = new TestThread1(); TestThread2 t2 = new TestThread2(); t1.startThread(); t

有谁能解释一下为什么第一个线程不工作,而第二个线程工作得很好:

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
内部,
thread
成员是
null
!!!最后,在
stopThread
内部,
thread
成员不是
null

有人能向我解释一下这里发生了什么事吗

TestThread1 t1 = new TestThread1();
t1.startThread();
这将简单地调用对象t1上的方法
startThread()
。在这个方法中,您正在创建一个新线程

thread = new TestThread1();
        thread.start();
但是对于这个线程,
Thread
实例变量为null(对于t1它不是null)

所以在这两种情况下,线程变量都应该为null

这将简单地调用对象t1上的方法
startThread()
。在这个方法中,您正在创建一个新线程

thread = new TestThread1();
        thread.start();
但是对于这个线程,
Thread
实例变量为null(对于t1它不是null)


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

正在创建两个TestThread1对象,一个启动,另一个停止


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

正在创建两个TestThread1对象,一个启动,另一个停止


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

因为在main方法中创建了Thread1对象。然后运行startThread,它在第一个Thread1对象中创建一个不同的Thread1对象,并将其设置为字段thread。然后启动第二个对象,该对象没有初始化自己的线程字段。在第二个对象上运行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()
   }
}

因为在main方法中创建了Thread1对象。然后运行startThread,它在第一个Thread1对象中创建一个不同的Thread1对象,并将其设置为字段thread。然后启动第二个对象,该对象没有初始化自己的线程字段。在第二个对象上运行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()
   }
}

这里有两个
testthread1t1
的实例:

  • 一个存储在
    t1
    局部变量中(在
    main
    方法中)
  • 一个存储在
    线程
    实例变量中(属于
    t1
t1
从未启动,
t1。线程
未启动

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

由于您正在启动
t1.thread
,其
run
方法使用
t1.thread.thread

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

更一般地说,您不能仅仅“杀死”线程本身,而是可以在方法中实现测试,以告诉它在某些情况下返回。您在第二个测试中所做的更接近于此(使用
而(!finished){…}
和一个volatile变量)

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

我会在(!finished&&!Thread.currentThread().isInterrupted()){…}时使用

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


根据循环中的内容(或者是否存在循环),您可能希望在不同的策略点使用类似于
if(finished | | Thread.currentThread().isInterrupted()){return;}
,在这里,您有两个
testthread1t1
的实例:

  • 一个存储在
    t1
    局部变量中(在
    main
    方法中)
  • 一个存储在
    线程
    实例变量中(属于
    t1
t1
从未启动,
t1。线程
未启动

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

由于您正在启动
t1.thread
,其
run
方法使用
t1.thread.thread

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

更一般地说,您不能仅仅“杀死”线程本身,而是可以在方法中实现测试,以告诉它在某些情况下返回。您在第二个测试中所做的更接近于此(使用
而(!finished){…}
和一个volatile变量)

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

我会在(!finished&&!Thread.currentThread().isInterrupted()){…}
时使用

(请注意
Thread.currentThread().isInterrupted()
Thread.interrupted()
之间的区别:它们可能看起来相似,但后者也会重置状态,您可能不希望这样做。)
 t1.startThread();
        t2.start();