Java-并发-Java线程中断与停止线程详解

Azura ·
更新时间:2024-09-20
· 795 次阅读

线程中断

  Java 中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。即“线程中断”并不是字面意思——线程真的中断了,而是设置了中断标志位为true。

thread.interrupt()

  该方法“中断线程”,但仅仅是会设置该线程的中断状态位为true,至于中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。
  线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会立即中断一个正在运行的线程,因此没有stop()方法带来的的问题。
  如果一个线程处于了阻塞状态(如线程调用了thread.sleep、thread.join、thread.wait、condition.await、以及可中断的通道上的 I/O 操作等方法后可进入阻塞状态),则在线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。
  synchronized在获锁的过程中的阻塞态是不能被中断的(可以设置标志位为true,但是不会抛出异常,因此不能从阻塞态中返回),与synchronized功能相似的lock.lock()方法也是一样,它也不可中断的,即如果发生死锁,意思是说如果产生了死锁,则不可能中断某个发生死锁而处于等待的线程。但是如果调用带超时的tryLock方法lock.tryLock(long timeout, TimeUnit unit),那么如果线程在等待时被中断,将抛出一个InterruptedException异常,这是一个非常有用的特性,因为它允许程序打破死锁。你也可以调用lock.lockInterruptibly()方法,它就相当于一个超时设为无限的tryLock方法。
  有一个特例是:被LockSupport.park()阻塞的线程也可以被中断,但是不会抛出异常.并且不会恢复标志位。

thread.isInterrupted()

  如果要检测当前线程是否被中断,请使用该方法,因为它不会清除中断标示位,即不会将中断标设置为false,就仅仅是检测状态而已。
  

public boolean isInterrupted() {return isInterrupted(false);} // ClearInterrupted false不重置标志位;true 重置标志位 private native boolean isInterrupted(boolean ClearInterrupted);

  判断是否中断还有一个方法Thread.interrupted(),但是请谨慎使用。该方法调用后会将中断标示位清除,即重新设置为false。并且该方法是Thread类的静态方法。

public static boolean interrupted() { return currentThread().isInterrupted(true); }

补充:
  interrupted()内部是获取当前调用线程的中断标志,而isInterrupted()则是获取调用isInterrupted()方法的实例对象的中断标志。下面验证:

public class interrupted { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) {} }); thread.start(); thread.interrupt(); //获取中断标志,获取子线程的中断标志 System.out.println("isInterrupted:" + thread.isInterrupted()); //获取中断标志并重置,虽然是thread.interrupted(),但实际上是获取主线程的中断标志,因为在主线程中调用的 System.out.println("interrupted:" + thread.interrupted()); //获取中断标志并重置,也是获取主线程的中断标志 System.out.println("interrupted:" + Thread.interrupted()); //获取中断标志,获取子线程的中断标志 System.out.println("interrupted:" + thread.isInterrupted()); } }

结果是

isInterrupted:true interrupted:false interrupted:false interrupted:true 应用

  当线程为了等待一些特定条件的到来时,一般会调用sleep函数、wait 系列函数或者join ()函数来阻塞挂起当前线程。比如一个线程调用了Thread.sleep(3000),那么调用线程会被阻塞,直到3s 后才会从阻塞状态变为激活状态。但是有可能在3s 内条件己被满足,如果一直等到3s后再返回有点琅费时间,这时候可以调用该线程的interrupt()方法, 强制sleep 方法抛出InterruptedException 异常而返回,线程恢复到激活状态。

public class InterruptTest { public static void main(String[] args) throws InterruptedException { Interrupt interrupt = new Interrupt(); Thread thread1 = new Thread(interrupt, "thread1"); Thread thread2 = new Thread(interrupt, "thread2"); thread1.start(); //主线程睡眠一秒,等待thread1获得锁,再开启thread2 Thread.currentThread().sleep(1000); thread2.start(); //主线程再次睡眠一秒,等待thread2由于获取不到锁而处于BLOCKED阻塞态(阻塞在synchronized处) Thread.currentThread().sleep(1000); //获取thread2状态 System.out.println(thread2.getName() + "状态:" + thread2.getState()); //尝试设置thread2的中断状态 thread2.interrupt(); //获取是否thread2是否处于中断,返回true,说明是, //但是没有抛出异常,此时我们便没办法处理这个出于阻塞态的线程 System.out.println(thread2.getName() + "是否是中断状态: " + thread2.isInterrupted()); System.out.println(thread2.getName() + "由于处于synchronized阻塞态,即使除以中断态,也没有抛出异常,无法结束"); System.out.println(); System.out.println("=========>开始处理这个问题"); System.out.println(); //获取thread1的状态,可以发现是TIMED_WAITING.并且是采用Thread.sleep(100)方式,可以被中断并抛出异常 System.out.println(thread1.getName() + "状态:" + thread1.getState()); //尝试设置thread1的中断状态 thread1.interrupt(); System.out.println(thread1.getName() + "是否是中断状态: " + thread1.isInterrupted()); //由于thread1 中断了等待,处理了异常,结束了运行,释放了锁。 //thred2获得了锁,进入同步块,执行sleep方法进入TIMED_WAITING状态 //由于thread2在前面设置了中断状态,因此也立即从sleep方法出抛出异常,并且正常结束线程。 } static class Interrupt implements Runnable { @Override public void run() { synchronized (Thread.class) { try { //处于等待 该等待方法可被中断,此时会中断等待,并且抛出异常,程序可以结束. Thread.sleep(100000); } catch (InterruptedException e) { //cache处理中断异常,结束等待,是的线程能正常结束运行 System.out.println(Thread.currentThread().getName() + "抛出了异常,结束了TIMED_WAITING,该线程可以返回"); } } } } } 停止线程

  这里的停止线程就是指结束线程。常见方法有以下几种

正常运行结束 程序运行结束,线程自动结束。 stop方法:但是已经过时,容易引发死锁。
  即刻停止run()方法中剩余的全部工作,包括在catch或finally语句中(如果不在try块中)并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
  会立即释放该线程所持有的所有的锁,导致数据得不到同步的处理,出现数据不一致的问题。如果以前受这些监视器保护的任何对象都处于不连贯状态,那么损坏的对象对其他线程可见,这有可能导致不安全的操作。 通过修改某个变量来终止线程。
  即在线程当中,定义一个标志位,通过改变标志位的形式,结束线程。 thread.interrupt()
  使用interrupt方法可以中断线程的等待状态,并不是直接把线程终止。我们可以配合抛出的异常来停止部分等待状态的线程
  如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int)方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException——当线程在活动之前或活动期间处于正在等待、休眠或占用状态且该线程被中断时,抛出该异常。我们可以在catch当中捕获到异常,并且改变标志位,或者跳出循环,即可结束线程。
  如果想要中断等待获得锁的线程的等待状态,那么该方法需要配合lock.lockInterruptibly()或者trylock()获取锁的方法!因为这种获取锁的方式允许等待锁的线程被中断。如果使用lock()方法,那么interrupt()方法无效,无法中断因为获取不到锁而等待(受阻)的线程,synchronized等待也是无法中断的。
作者:Data Mining



JAVA 并发 中断

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章