首先如果我们问java如何去停掉一个线程,大家肯定会说stop嘛,但是也有一些朋友提出质疑,stop安全吗?
没错stop是不提倡的,
不提倡的stop()方法
臭名昭著的stop()停止线程的方法已不提倡使用了,原因是什么呢? 当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,假如一个线程正在执行:synchronized void { x = 3; y = 4;}
由于方法是同步的,多个线程访问时总能保证x,y被同时赋值,而如果一个线程正在执行到x = 3;时,被调用了 stop()方法,即使在同步块中,它也干脆地stop了,这样就产生了不完整的残废数据。而多线程编程中最最基础的条件要保证数据的完整性,所以请忘记 线程的stop方法,以后我们再也不要说“停止线程”了。
如何才能“结束”一个线程?
那么什么方式才是我们提倡的呢,用interrupt()?
那么如何能确保线程真正停止?在线程同步的时候我们有一个叫“二次惰性检测”(double check),能在提高效率的基础上又确保线程真正中同步控制中。那么我把线程正确退出的方法称为“双重安全退出”,即不以isInterrupted ()为循环条件。而以一个标记作为循环条件:
- package org.leadfar;
- public class MyThread04 extends Thread {
- private boolean stop = false;
- public MyThread04(String threadName) {
- super(threadName);
- }
- @Override
- public void run() {
- for (int j = 0; j < 100; j++) {
- if(this.isInterrupted()) break;
- System.out.println(Thread.currentThread().getName()+":"+j);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public void setStop() {
- this.stop = true;
- }
- //第一个线程
- public static void main(String[] args) {
- MyThread04 t = new MyThread04("辅线程");
- t.start();
- for (int i = 0; i < 10; i++) {
- System.out.println(Thread.currentThread().getName()+":"+i);
- }
- System.out.println("....................");
- t.interrupt();
- }
- }
但这个很可能不会终止线程,因为当我们终止这个线程时很可能就会发生InterruptedException异常,当有这个异常发生时我们设置的终断状态也会被清除,所以我们要终断某个线程应采用以下这个方法:
- package org.leadfar;
- public class MyThread04 extends Thread {
- private boolean stop = false;
- public MyThread04(String threadName) {
- super(threadName);
- }
- @Override
- public void run() {
- for (int j = 0; j < 100; j++) {
- if(stop) break;
- System.out.println(Thread.currentThread().getName()+":"+j);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public void setStop() {
- this.stop = true;
- }
- //第一个线程
- public static void main(String[] args) {
- MyThread04 t = new MyThread04("辅线程");
- t.start();
- for (int i = 0; i < 10; i++) {
- System.out.println(Thread.currentThread().getName()+":"+i);
- }
- System.out.println("....................");
- t.setStop();
- }
- }
通过设置一个我们自己的标识来达到终端某个线程。
讲到这里大家一定就认为这样一定没有问题了对吗,答案是不,
其实大家可以想想我们设置的那个stop变量一定会很老实的被我们修改吗,想想一个简单的例子
//线程1 boolean stop = false ; while (!stop){ doSomething(); } //线程2 stop = true ; |
这段代码是很典型的一段代码,很多人在中断线程时可能都会采用这种标记办法。但是事实上,这段代码会完全运行正确么?即一定会将线程中断么?不一定,也许在大多数时候,这个代码能够把线程中断,但是也有可能会导致无法中断线程(虽然这个可能性很小,但是只要一旦发生这种情况就会造成死循环了)。
下面解释一下这段代码为何有可能导致无法中断线程。在前面已经解释过,每个线程在运行过程中都有自己的工作内存,那么线程1在运行的时候,会将stop变量的值拷贝一份放在自己的工作内存当中。
那么当线程2更改了stop变量的值之后,但是还没来得及写入主存当中,线程2转去做其他事情了,那么线程1由于不知道线程2对stop变量的更改,因此还会一直循环下去。
所以在这里我们引入了一个关键字volatile,其实这里还有一些java的其他概念原子性,可见性,有序性,有兴趣的朋友可以了解一下
以及volatile
好的那么我们的线程到底怎么才能被我们停掉,相比大家也能感觉到我们可以在stop关键词上加上volatile关键字啊,没错就是这样的方式。其实我们在终止线程的时候有很多复杂的情况,这里就不一一列举了。