zoukankan      html  css  js  c++  java
  • Java线程的中断

    引言

    Java没有提供任何机制来安全地终止线程,但提供了中断机制,即thread.interrupt()方法。线程中断是一种协作式的机制,并不是说调用了中断方法之后目标线程一定会立即中断,而是发送了一个中断请求给目标线程,目标线程会自行在某个取消点中断自己。这种设定很有必要,因为如果不论线程执行到何种情况都立即响应中断的话,很容易造成某些对象状态不一致的情况出现。

    正文

    一、中断相关的方法介绍

    涉及到中断的线程基础方法有三个:interrupt()、isInterrupted()、interrupted(),它们都位于Thread类下。Thread类下还有一个

    interrupt()方法:对目标线程发送中断请求,看其源码会发现最终是调用了一个本地方法实现的线程中断;

    interrupted()方法:返回目标线程是否中断的布尔值(通过本地方法实现),且返回后会重置中断状态为未中断;

    isInterrupted()方法:该方法返回的是线程中断与否的布尔值(通过本地方法实现),不会重置中断状态;

    二、线程中断

    线程中断可以按中断时线程状态分为两类,一类是运行时线程的中断,一类是阻塞或等待线程的中断。有中断时,运行时的线程会在某个取消点中断执行,而处于阻塞或者等待状态的线程大多会立即响应中断,比如上一篇文章中提到的join、sleep等方法,这些方法在抛出中断异常的错误后,会重置线程中断状态为未中断。但注意,获取独占锁的阻塞状态与BIO的阻塞状态不会响应中断。而在JUC包中有在加锁阻塞的过程中响应中断的方法,比如lockInterruptibly()。

    下面从三个问题来讲述线程中断

    1、线程中断的目的是什么?

    为什么要进行线程中断?有时是由于对于某种特定情况,我们知道当前线程无需继续执行下去,此时可以中断此线程;有时是遇到某些异常,需要中断线程。具体什么目的,还要看具体场景,但线程中断的需求已经摆在那里,肯定需要。

    2、要如何处理线程中断?

    通常的处理方式有两种,如果是业务层面的代码,则只需要做好中断线程之后的业务逻辑处理即可,而如果是偏底层功能的线程中断,则尽量将中断异常抛出(或者在catch中重新调用interrupt()来中断线程),以告知上层方法本线程的中断经历。

    3、JUC中对中断的处理举例

    JUC中ReentrantLock常用的加锁方法是lock(),还有一个响应中断的加锁方法lockInterruptibly()

    lock()方法中的acquire(int arg)方法如下所示:

    1 public final void acquire(int arg) {
    2         if (!tryAcquire(arg) &&
    3             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    4             selfInterrupt();
    5 }

    在acquireQueued中会对线程的中断状态做判断,如果中断了则返回true,进入selfInterrupt()方法,恢复线程的中断状态。但注意此处是在获取到锁之后再响应中断,在获取到锁之前不会做出响应。

    1 static void selfInterrupt() {
    2         Thread.currentThread().interrupt();
    3 }

    而看lockInterruptibly()方法:

     1 public void lockInterruptibly() throws InterruptedException {
     2         sync.acquireInterruptibly(1);
     3     }
     4 
     5 public final void acquireInterruptibly(int arg)
     6             throws InterruptedException {
     7         if (Thread.interrupted())
     8             throw new InterruptedException();
     9         if (!tryAcquire(arg))
    10             doAcquireInterruptibly(arg);
    11     }

    它会先查看中断状态,再获取锁。而如果在获取锁的过程中中断过,则会在doAcquireInterruptibly方法中抛出中断异常。

     下面是我在本地模拟的lock阻塞中断:

     1 public class ReentrantLockDemo {
     2     public static void main(String[] args) throws InterruptedException {
     3         System.out.println("main start");
     4         Thread thread1 = new Thread(new LockThreadDemo());
     5         Thread thread2 = new Thread(new LockThreadDemo());
     6         thread1.start();
     7         Thread.sleep(1000); // 确保thread1获取到了锁
     8         thread2.start(); // 此时thread2处于获取锁的阻塞状态
     9         thread2.interrupt();
    10         System.out.println("main end");
    11     }
    12 }
    13 
    14 class LockThreadDemo implements Runnable {
    15     public static ReentrantLock lock = new ReentrantLock();
    16     @Override
    17     public void run() {
    18         System.out.println(Thread.currentThread().getName() + "runnable run");
    19         try {
    20             lock.lock();
    21             System.out.println(Thread.currentThread().getName() + "开始睡眠");
    22             Thread.sleep(5000);
    23             System.out.println(Thread.currentThread().getName() + "睡了5秒");
    24         } catch (Exception e) {
    25             System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
    26         } finally {
    27             lock.unlock();
    28         }
    29         System.out.println(Thread.currentThread().getName() + " over");
    30     }
    31 }

    执行结果为:

    main start
    Thread-0runnable run
    Thread-0开始睡眠
    main end
    Thread-1runnable run
    Thread-0睡了5秒
    Thread-0 over
    Thread-1开始睡眠
    Thread-1runnable exception:java.lang.InterruptedException: sleep interrupted
    Thread-1 over

    可以看到中断了并没有对获取锁产生影响,最后是sleep方法响应的中断。

    下面是我在本地模拟的lockInterruptibly()阻塞中断:

     1 public class ReentrantLockInterruptableDemo {
     2     public static void main(String[] args) throws InterruptedException {
     3         System.out.println("main start");
     4         Thread thread1 = new Thread(new LockThreadInterruptableDemo());
     5         Thread thread2 = new Thread(new LockThreadInterruptableDemo());
     6         thread1.start();
     7         Thread.sleep(1000); // 确保thread1获取到了锁
     8         thread2.start(); // 此时thread2处于获取锁的阻塞状态
     9         thread2.interrupt();
    10         System.out.println("main end");
    11     }
    12 }
    13 
    14 class LockThreadInterruptableDemo implements Runnable {
    15     public static ReentrantLock lock = new ReentrantLock();
    16     @Override
    17     public void run() {
    18         System.out.println(Thread.currentThread().getName() + "runnable run");
    19         try {
    20             lock.lockInterruptibly();
    21             System.out.println(Thread.currentThread().getName() + "开始睡眠");
    22             Thread.sleep(5000);
    23             System.out.println(Thread.currentThread().getName() + "睡了5秒");
    24         } catch (Exception e) {
    25             System.out.println(Thread.currentThread().getName() + "runnable exception:" + e);
    26         } finally {
    27             try {
    28                 lock.unlock();
    29             } catch (IllegalMonitorStateException e) {
    30                 System.out.println("因线程" + Thread.currentThread().getName() + "提前中断导致未获取到锁");
    31             }
    32         }
    33         System.out.println(Thread.currentThread().getName() + " over");
    34     }
    35 }

    结果为:

    main start
    Thread-0runnable run
    Thread-0开始睡眠
    main end
    Thread-1runnable run
    Thread-1runnable exception:java.lang.InterruptedException
    因线程Thread-1提前中断导致未获取到锁
    Thread-1 over
    Thread-0睡了5秒
    Thread-0 over

    结束语

    对于线程中断的处理比较常见,尤其是涉及到多线程的框架、组件中。而能否处理好线程中断的各种情况,则体现出一个程序员对多线程掌握的熟练情况。每天进步一点,日拱一卒,加油!

  • 相关阅读:
    智能推荐算法演变及学习笔记(三):CTR预估模型综述
    从中国农业银行“雅典娜杯”数据挖掘大赛看金融行业数据分析与建模方法
    智能推荐算法演变及学习笔记(二):基于图模型的智能推荐(含知识图谱/图神经网络)
    (设计模式专题3)模板方法模式
    (设计模式专题2)策略模式
    (设计模式专题1)为什么要使用设计模式?
    关于macOS上常用操作命令(持续更新)
    记录下关于RabbitMQ常用知识点(持续更新)
    EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
    SpringCloud教程二:Ribbon(Finchley版)
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/10925853.html
Copyright © 2011-2022 走看看