保留好你做过的所有源文件——那是你最好的积攒之一。

线程启动结束后,在运行时可能须要终止,Java提供的终止办法只有一个stop,然而我不倡议应用这个办法,因为它有以下三个问题:

(1)stop办法是过期的从Java编码规定来说,曾经过期的办法不倡议采纳。

(2)stop办法会导致代码逻辑不残缺stop办法是一种“歹意”的中断,一旦执行stop办法,即终止以后正在运行的线程,不论线程逻辑是否残缺,这是十分危险的。看如下的代码:

public static void main(String[] args) throws Exception {    // 子线程    Thread thread = new Thread() {        @Override        public void run() {            try {                // 子线程休眠一秒                Thread.sleep(1000);            } catch (InterruptedException e) {                // 异样解决            }        }    };    // 启动线程    thread.start();    // 主线程休眠一秒    Thread.sleep(1000);    // 子线程进行    thread.stop();}

这段代码的逻辑是这样的:子线程是一个匿名外部类,它的run办法在执行时会休眠1秒钟,而后再执行后续的逻辑,而主线程则是休眠0.1秒后终止子线程的运行,也就是说,JVM在执行thread.stop()时,子线程还在执行sleep(1000),此时stop办法会革除栈内信息,完结该线程,这也就导致了run办法的逻辑不残缺,输入语句println代表的是一段逻辑,可能十分重要,比方子线程的主逻辑、资源回收、情景初始化等,然而因为stop线程了,这些就都不再执行,于是就产生了业务逻辑不残缺的状况。

这是极度危险的,因为咱们不晓得子线程会在什么时候被终止,stop连根本的逻辑完整性都无奈保障。而且此种操作也是十分荫蔽的,子线程执行到何处会被敞开很难定位,这为当前的保护带来了很多麻烦。

(3)stop办法会毁坏原子逻辑多线程为了解决共享资源抢占的问题,应用了锁概念,防止资源不同步,然而正因而起因,stop办法却会带来更大的麻烦:它会抛弃所有的锁,导致原子逻辑受损。例如有这样一段程序:

class MultiThread implements Runnable {    int a = 0;    @Override    public void run() {        // 同步代码块,保障原子操作        synchronized ("") {            a++; // 自增            try {                // 线程休眠0.1秒                Thread.sleep(100);            } catah(InterruptedException e) {                e.printStackTrace();            }            a--; // 自减            String tn = Thread.currentThread().getName();            System.out.println(tn + ":a=" + a);        }    }}

MultiThread实现了Runnable接口,具备多线程能力,其中run办法中加上了synchronized代码块,示意外部是原子逻辑,它会先自增而后再自缩小,依照synchronized同步代码块的规定来解决,此时无论启动多少个线程,打印进去的后果都应该是a=0,然而如果有一个正在执行的线程被stop,就会毁坏这种原子逻辑,代码如下:

public static void main(String[] args) {    MultiThread thread = new MultiThread();    Thread thread1 = new Thread(thread);    // 启动thread1线程    thread1.start();    for (int i = 0; i < 5; i++) {        new Thread(thread).start();    }    // 进行thread1线程    thread1.stop();}

首先要阐明的是所有线程共享了一个MultiThread的实例变量t,其次因为在run办法中退出了同步代码块,所以只能有一个线程进入到synchronized块中。此段代码的执行程序如下:

1)线程t1启动,并执行run办法,因为没有其余线程持同步代码块的锁,所以t1线程执行自加后执行到sleep办法即开始休眠,此时a=1。

2)JVM又启动了5个线程,也同时运行run办法,因为synchronized关键字的阻塞作用,这5个线程不能执行自增和自减操作,期待t1线程锁开释。

3)主线程执行了t1.stop办法,终止了t1线程,留神,因为a变量是所有线程共享的,所以其余5个线程取得的a变量也是1。

4)其余5个线程顺次取得CPU执行机会,打印出a值。

剖析了这么多,置信读者也明确了输入的后果,后果如下:

Thread-5:a=1Thread-4:a=1Thread-3:a=1Thread-2:a=1Thread-1:a=1

本来冀望synchronized同步代码块中的逻辑都是原子逻辑,不受外界线程的烦扰,然而后果却呈现原子逻辑被毁坏的状况,这也是stop办法被废除的一个重要起因:毁坏了原子逻辑。

既然终止一个线程不能应用stop办法,那怎样才能终止一个正在运行的线程呢?答案也很简略,应用自定义的标记位决定线程的执行状况,代码如下:

class SafeStopThread extends Thread {    // 此变量必须加上volatile    private volatile boolean stop = false;    @Override    public void run() {        // 判断线程体是否运行        while (stop) {            // Do Something        }    }    // 线程终止    public void terminate() {        stop = true;    }} 

这是很简略的方法,在线程体中判断是否须要进行运行,即可保障线程体的逻辑完整性,而且也不会毁坏原子逻辑。可能有读者对Java API比拟相熟,于是提出疑难:Thread不是还提供了interrupt中断线程的办法吗?这个办法可不是过期办法,那能够应用吗?它能够终止一个线程吗?

十分好的问题,interrupt,名字看上去很像是终止一个线程的办法,然而我能够很明确地通知你,它不是,它不能终止一个正在执行着的线程,它只是批改中断标记而已,例如上面一段代码:

public static void main(String[] args) {    Thread thread = new Thread() {        public void run() {            // 线程始终运行            while (true) {                System.out.println("Running...");            }        }    };    // 启动thread线程    thread.start();    // 中断thread线程    thread.interrupt();}

执行这段代码,你会发现始终有Running在输入,永远不会进行,仿佛执行了interrupt没有任何变动,那是因为interrupt办法不能终止一个线程状态,它只会扭转中断标记位(如果在t1.interrupt()前后输入t1.isInterrupted()则会发现别离输入了false和true),如果须要终止该线程,还须要自行进行判断,例如咱们能够应用interrupt编写出更加简洁、平安的终止线程代码:

class SafeStopThread extends Thread {    @Override    public void run() {        // 判断线程体是否运行        while (!isInterrupted()) {            // Do Something        }    }}

总之,如果冀望终止一个正在运行的线程,则不能应用曾经过期的stop办法,须要自行编码实现,如此即可保障原子逻辑不被毁坏,代码逻辑不会出现异常。当然,如果咱们应用的是线程池(比方ThreadPoolExecutor类),那么能够通过shutdown办法逐渐敞开池中的线程,它采纳的是比拟温和、平安的敞开线程办法,齐全不会产生相似stop办法的弊病。