多线程(四) 如何中止线程 多线程(四) 如何中止线程

  在Thread类中提供了能够中止线程的方法(包括杀死和挂起):html

    @Deprecated
    public final void stop(){}

    @Deprecated
    public final void suspend(){}
    

   stop 和 suspend 添加的有Deprecated注释,也便是该方法已经废弃使用。那么为何会不建议使用这两种方法呢?还有没有其余中止线程的方法?安全

一、stop()会当即杀死线程,没法保证原子操做可以顺利完成,存在数据错误的风险。不管线程执行到哪里、是否加锁,stop会当即中止线程运行,直接退出。多线程

   以下代码:ide

 int account01 = 10;
    int account02= 0;
    Object lock = new Object();
    
    public void testStop() {
        class StopRunnable implements Runnable {
            @Override
            public void run() {
                //要求 account01 + account02 =10  恒成立
                while (true) {
                    synchronized (lock) {//加锁保证操做的原子性
                        account01--;
                        sleep(200);//睡眠模拟执行过程
                        account02++;
                    }
                }
            }
        }
        Thread thread = new Thread(new StopRunnable());
        thread.start();
        sleep(1300);
        thread.stop();
        System.out.println("account01: " + account01 + "\naccount02: " + account02);
    }
    private void sleep(int time){ try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } }

 运行结果以下:post

account01: 3
account02: 6

 很明显没有保证二者的和为10。说明在线程循环过程当中,最后一个加锁循环体没有完整执行结束,数据发生错误。除此以外,在执行stop方法以后,线程会当即释放锁,这一样也会致使原子操做失败数据异常。url

官方注释:spa

Forces the thread to stop executing.
It is permitted to stop a thread that has not yet been started.
If the thread is eventually started, it immediately terminates.

 二、suspend()并未杀死线程,只是把线程挂起,中止线程的运行。但挂起以后并不会释放锁,这样,若是有其它多个线程在等待该锁,则程序将会发生死锁。线程

  以下代码:code

    int account01 = 10;
    int account02= 0;
    Object lock = new Object();

    public void testStop() {
        class StopRunnable implements Runnable {
            @Override
            public void run() {
                //要求 account01 + account02 =10  恒成立
                for(int i =0;i<5;i++){
                    synchronized (lock) {//加锁保证操做的原子性
                        account01--;
                        System.out.println("....."+Thread.currentThread().getName());//为了看到线程中止添加输出线程名称操做
                        sleep(200);//睡眠200ms
                        account02++;
                    }
                }
            }
        }
        Thread thread01 = new Thread(new StopRunnable());
        thread01.setName("thread01");
        Thread thread02 = new Thread(new StopRunnable());
        thread02.setName("thread02");

        thread01.start();
        thread02.start();

        sleep(500);
        thread01.suspend();
        while (true){
            sleep(1000);
            System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive());
        }
    }

运行结果以下:  htm

.....thread01
.....thread01
.....thread01
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true

由结果能够看出,thread01一直在运行,而thread02一次也没有执行到run方法。而后在执行thread01.suspend()以后,两个线程都中止了运行run方法。但同时两个线程都没有死亡。

在代码中只对thread01执行了suspend,那么若是thread02获取到锁则应该继续由thread02执行run方法,但并无,说明锁lock一直由thread01持有,在挂起以后并未释放。

其实在使用suspend()方法的时候是须要配合resume()同时使用的。

     ....
     ....
sleep(
500); thread01.suspend(); int time = 0;//添加time计数,在循环三次以后释放 while (true){ time ++; if(time ==3){ thread01.resume();//释放线程 } sleep(1000); System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive()); }

 执行结果以下:  

.....thread01
.....thread01
.....thread01
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true .....thread01 //释放以后继续运行 .....thread01 .....thread02 //thread01释放锁,thread02获取锁继续运行 .....thread02
.....thread02
account01: 2 account02: 7 thread01 isAlive:false thread02 isAlive:true  //thread01 死亡,thread02活着
.....thread02
.....thread02
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false

 能够看出,thread01.resume()以后thread01继续运行,而后运行结束释放锁以后,thread02接着运行起来,这时能够看到thread01已经死亡,而thread02依旧活着。直至两个线程所有结束。若是正常使用suspend()和resume()并不会出现太大问题,只是在涉及到锁的时候久须要格外当心了。r若是没有使用到锁,那么其中一个线程的挂起并不会影响到其余线程的执行。

对于public void interrupt(){}方法,该方法只是对阻塞状态的线程(seleep、wait、join和IO/NIO操做)进行中断操做,在调用interrupt方法的时候,若是线程处于阻塞状态则会抛出InterruptedException/ClosedByInterruptException。在程序中只需对异常进行捕获,并不影响后续的操做。对于未处于阻塞状态的线程,调用interrupt方法没有任何影响。因此interrupt严格意义上说并不属于中止线程的方法。

那么,到底该如何安全的中止线程呢?

  遵循的规则:让线程本身中止本身

     两种方法:一、线程任务执行完成,顺利结束退出。二、设置终止标志位,在循环的时候进行终止标志位检测,若是设置为终止状态则return结束线程。

 例如:一、线程执行完成自动退出:

            public void run() {
                for(int i = 0;i<10;i++){//循环十次以后run方法结束自动结束线程
                    System.out.println(Thread.currentThread().getName());
                }
            }

 二、设置终止标志位。

    boolean isStop = false;//终止标志位 当须要结束线程时,更改成true
    public void testInterrupt(){
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    if(isStop){//当须要结束线程的时候终止线程
                        //doSomething  进行一些收尾工做
                        return;
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            }
        });

  设置终止标志位的时候线程不会当即终止,只有当循环到标志位判断的时候才会执行退出操做,这样就能够在循环体中合适的地方执行退出逻辑,能够保证原子操做的顺利完成以及锁的释放。

 对于ExecutorService的void shutdown();方法,该方法只是中止线程池接受新的任务同时等待已提交线程结束,并不会中止线程。因此该方法不属于中止线程的方法。

 

=========================================

 

原文连接:多线程(四) 如何中止线程 转载请注明出处!

 

=========================================

 

---end