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

  在Thread类中提供了可以停止线程的方法(包括杀死和挂起):

    @Deprecated
    public final void stop(){}
    @Deprecated
    public final void suspend(){}
    
   stop 和 suspend 添加的有Deprecated注释,也即是该方法已经废弃使用。那么为什么会不建议使用这两种方法呢?还有没有其他停止线程的方法?

1、stop()会立即杀死线程,无法保证原子操作能够顺利完成,存在数据错误的风险。无论线程执行到哪里、是否加锁,stop会立即停止线程运行,直接退出。

   如下代码:

 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();
        }
    }

运行结果如下:

account01: 3
account02: 6

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

官方注释:

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.

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

  如下代码:

    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());
        }
    }

运行结果如下:  

.....thread01.....thread01.....thread01account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:trueaccount01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:trueaccount01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:trueaccount01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:trueaccount01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:trueaccount01: 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严格意义上说并不属于停止线程的方法。

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

  遵循的规则:让线程自己停止自己

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

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

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

2、设置终止标志位。

    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

时间: 2024-10-13 12:28:16

多线程(四) 如何停止线程的相关文章

多线程---其他方法 停止线程、守护线程、join方法

第三方停止线程: 原来是stop(),因为该方法有些问题,所以被interrupt()方法取代,它的用途跟机制是 当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以让操作标记来结束线程. Thread类提供了interrupt()方法. 守护线程: 线程对象.setDaemon():该方法表示,前台线程(主线程) 一结束,后台线程(后台运行线程)立即结束. Join方法: 等待该线程终止.就是说在主线程运行时候,读到了该线程的开始阶段,

java多线程(四)-自定义线程池

当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的. 1 public static ExecutorService newCachedThreadPool() { 2 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 3 60L, TimeUnit.SECONDS, 4 new Synchro

java多线程之停止线程

在多线程开发中停止线程是很重要的技术点.停止线程在Java语言中并不像break语句那样干脆,需要一些技巧性的处理. 一.  异常法 采用异常法来停止一个线程,首先我们需要了解一下两个方法的用法: 1.interrupt()方法 public class MyThread extends Thread{ @Override public void run() { for (int i = 1; i <= 10000; i++) { System.out.println("i="+

JAVA-初步认识-第十四章-多线程-停止线程方式-定义标记

一. 线程既然开启了,运行了,冻结又恢复运行了,那什么时候消亡呢? 怎么来停止线程呢?不能一直在运行. 线程怎么停,线程自己最清楚.在Thread类中,提供了stop方法, 本来线程持有一个锁,只要stop,它就什么都在放,这是强制操作,无论出于什么状态,强制将其搞定.本来wait,这那儿的,stop一下子就没了. stop方法做的不是状态切换了,而是消失. 线程停止,其实只有一种,就是run方法结束. 线程一旦没有自己运行的代码了,任务没有了,线程自动就结束了.可是线程的任务怎么能没有呢? 标

Java多线程(四)、线程池(转)

Java多线程(四).线程池 分类: javaSE综合知识点 2012-09-19 17:46 3943人阅读 评论(1) 收藏 举报 系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似的是,线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束后,该线

多线程(停止线程)

多线程的停止方法stop已经过时,所以停止线程的方法只有一种,run方法结束.因为多线程 运行的代码通常都是循环结构的,只要控制住循环就可以让run方法结束,也就是线程结束.(使用标记控制循环) PS: 特殊情况: 当线程处于了冻结状态,就不会读取到标记,那么线程就不会结束. 当没有指定的方法让冻结的线程恢复运行时,需要对冻结状态进行清除, Thread类提供了专门用于清除线程冻结状态的方法——interrupt 停止线程示例: class stopThreadDemo { public sta

JAVA学习第二十八课(多线程(七))- 停止线程和多线程面试题

重点掌握 /* * wait 和 sleep 区别? * 1.wait可以指定时间也可以不指定 * sleep必须指定时间 * 2.在同步中,对CPU的执行权和锁的处理不同 * wait释放执行权,释放锁    sleep释放执行权,不释放锁 */ //同步里具备执行资格的线程不止一个,但是能得到锁的只有一个,所以能执行的也只有一个 一.停止线程的方式 不可能让线程一直在运行,所以需要让线程停止 1.定义循环结束标记 一般而言,线程运行代码都是循环的,只要控制了循环就可以结束任务 2.使用int

从头认识多线程-1.9 迫使线程停止的方法-return法

这一章节我们来讨论一下还有一种停止线程的方法-return 1.在主线程上面return,是把全部在执行的线程都停掉 package com.ray.deepintothread.ch01.topic_9; public class StopByReturn { public static void main(String[] args) throws InterruptedException { ThreadFive threadFive = new ThreadFive(); Thread

二、java多线程编程核心技术之(笔记)——如何停止线程?

1.异常法 public class MyThread extends Thread { @Override public void run() { super.run(); try { for (int i = 0; i < 5000000; i++) { if(this.interrupted()){ System.out.println("我要停止了....."); throw new InterruptedException(); \\抛出异常 } System.out.