JAVA线程的学习3(如何正确的停止线程?)

如何正确停止线程?

使用interrupt来通知,而不是强制

通常线程会在什么情况下停止

1.所有代码执行完毕
2.有异常出现

正确停止方法:interrupt

1.通常情况下如何停止?
在run()方法中的while语句中加入!Thread.currentThread().isInterrupted()去监测线程是否已经被中断了。

package threadcoreknowledge.stopthreads;

/**
 * 描述:  run方法内没有sleep或wait方法时,停止线程
 */
public class RightWayStopThreadWithoutSleep implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        Thread.sleep(100);
        thread.interrupt();
    }

    @Override
    public void run() {
        int num = 0;
        while(!Thread.currentThread().isInterrupted()&&num<Integer.MAX_VALUE/2){
            if(num%10000==0){
                System.out.println(num+"是10000的倍数。");
            }
            num++;
        }
        System.out.println("任务运行结束了");
    }
}

2. 线程被阻塞如何停止?
会抛出sleep interrupted异常,响应中断信号。

package threadcoreknowledge.stopthreads;

/**
* 描述: 带有sleep()的中断线程的写法
*/
public class RightWayStopThreadWithSleep {
   public static void main(String[] args) throws InterruptedException {
       Runnable runnable = ()->{
           int num = 0;
           try{
           while(num <= 300 && !Thread.currentThread().isInterrupted()){
               if(num%100==0){
                   System.out.println(num+"是100的倍数");
               }
               num++;
           }
           Thread.sleep(1000);
       }catch (InterruptedException e){
               e.printStackTrace();
           }
       };
       Thread thread= new Thread(runnable);
       thread.start();
       Thread.sleep(500);
       thread.interrupt();
   }
}

运行结果:

E:\.jdks\corretto-1.8.0_252\bin\java.exe "-javaagent:E:\IntelliJ IDEA 2019.3.5\lib\idea_rt.jar=57184:E:\IntelliJ IDEA 2019.3.5\bin" -Dfile.encoding=UTF-8 -classpath E:\.jdks\corretto-1.8.0_252\jre\lib\charsets.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\access-bridge-64.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\cldrdata.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\dnsns.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\jaccess.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\jfxrt.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\localedata.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\nashorn.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunec.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunjce_provider.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunmscapi.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunpkcs11.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\zipfs.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jce.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jfxswt.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jsse.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\management-agent.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\resources.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\rt.jar;E:\workspace\thread-study\out\production\demo01 threadcoreknowledge.stopthreads.RightWayStopThreadWithSleep
0100的倍数
100100的倍数
200100的倍数
300100的倍数
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadcoreknowledge.stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:14)
	at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

3. 如果线程在每次迭代后都阻塞如何停止?
不需要再加入!Thread.currentThread().isInterrupted()判断条件,因为在sleep()过程中会响应中断。

package threadcoreknowledge.stopthreads;

/**
* 描述: 如果在执行过程中,每次循环都会调用sleep或者wait等方法,那么不需要每次迭代都检查是否已中断。
*/
public class RightWayStopThreadWithSleepEvertLoop {
   public static void main(String[] args) throws InterruptedException {
       Runnable runnable = ()->{
           int num = 0;
           try{
               while(num <= 10000 /*&& !Thread.currentThread().isInterrupted()*/){
                   if(num%100==0){
                       System.out.println(num+"是100的倍数");
                   }
                   num++;
                   Thread.sleep(10);
               }
           }catch (InterruptedException e){
               e.printStackTrace();
           }
       };
       Thread thread= new Thread(runnable);
       thread.start();
       Thread.sleep(5000);
       thread.interrupt();
   }
}

**

while内try/catch的问题

如果while里面放try/catch,会使中断失效

package threadcoreknowledge.stopthreads;

/**
 * 描述: 如果while里面放try/catch,会导致中断失效
 */
public class CantInterrupt {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = ()->{
            int num = 0;
            while (num <= 10000&&!Thread.currentThread().isInterrupted()){
                if(num %100 == 0){
                    System.out.println(num+"是100的倍数");
                }
                num++;
                try {
                    Thread.sleep(10);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };
        Thread thread= new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
    }
}

运行结果

E:\.jdks\corretto-1.8.0_252\bin\java.exe "-javaagent:E:\IntelliJ IDEA 2019.3.5\lib\idea_rt.jar=53159:E:\IntelliJ IDEA 2019.3.5\bin" -Dfile.encoding=UTF-8 -classpath E:\.jdks\corretto-1.8.0_252\jre\lib\charsets.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\access-bridge-64.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\cldrdata.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\dnsns.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\jaccess.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\jfxrt.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\localedata.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\nashorn.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunec.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunjce_provider.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunmscapi.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\sunpkcs11.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\ext\zipfs.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jce.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jfxswt.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\jsse.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\management-agent.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\resources.jar;E:\.jdks\corretto-1.8.0_252\jre\lib\rt.jar;E:\workspace\thread-study\out\production\demo01 threadcoreknowledge.stopthreads.CantInterrupt
0100的倍数
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadcoreknowledge.stopthreads.CantInterrupt.lambda$main$0(CantInterrupt.java:16)
	at java.lang.Thread.run(Thread.java:748)
100100的倍数
200100的倍数

虽然报了异常,但是线程并没有真正停止。原因是Java中sleep一旦响应了中断,就会把interrupt的标记位清除,所以在sleep过程中收到了中断,catch住了也打印出了异常栈,但是interrupt标记位被清除了,所以在while的判断语句中也检查不到任何被中断过的迹象,所以打断不了线程。

实际开发中的两种最佳实践

1.优先选择传递中断:

在方法的签名中抛出异常,那么在run()中就会强制try/catch。throwInMethod()方法应该throws异常而不是直接try/catch处理异常,然后run()方法调用throwInMethod()方法就可以使用try/catch处理接收到中断信号并进行后续的处理。

package threadcoreknowledge.stopthreads;

/**
 * 描述:
 * 最佳实践: catch了InterruptedException之后的优先选择:在方法签名中抛出异常
 * 那么在run()中就会强制try/catch
 */
public class RightWayStopThreadInProd implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("WDNMD");
            try {
                throwInMethod();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private void throwInMethod() throws InterruptedException {
//        try {
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

2.不想传递或者无法传递中断的时候:恢复中断

在catch子语句中调用Thread.currentThread().interrupt()来恢复中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断。

package threadcoreknowledge.stopthreads;

/**
 * 描述: 最佳实践2:在catch子语句中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断
 * 回到刚才RightWayStopThreadInProd补上中断,让它跳出
 */
public class RightWayStopThreadInProd2 implements Runnable{

    @Override
    public void run() {
        while(true){
            if( Thread.currentThread().isInterrupted()) {
                System.out.println("Interrupted,程序运行结束");
                break;
            }
            reInterrupt();
        }
    }
    private void reInterrupt(){
//        try {
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

*不应该屏蔽中断!

响应中断的方法总结表:

  1. object.wait()/wait(long)/wait(long,int)
  2. Thread.sleep(long)/sleep(long,int)
  3. Thread.join()/join(long)/join(long,int)
  4. java.util.concurrent.BlokingQueue.take()/put(E)
  5. java.util.concurrent.locks.Lock.lockInterruptibly()
  6. java.util.concurrent.CountDownLatch.await()
  7. java.util.concurrent.CyclicBarrier.await()
  8. java.util.concurrent.Exchanger.exchanger(V)
  9. java.nio.channels.InterruptibleChannel相关方法
  10. java.nio.channels.Selector的相关方法

使用interrupt来停止线程的好处

被中断的线程拥有如何响应中断的权利,某些线程的代码非常重要的,需要等线程处理完之后由线程自己主动终止。

错误的停止线程的方法

1.被弃用的stop,suspend和resume方法

stop会导致线程突然停止,没法完成一个基本单位的操作,会造成脏数据。suspend和resume会让线程挂起,在恢复之前不会释放,会带着锁去休息,如果其他线程不及时唤醒它,或者其他线程需要这把锁来唤醒的话就会造成死锁。

2.用volatile设置boolean标记位

这个方法有局限性,在一定的情况下可行,在特定的情况下会失效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值