Java 并发:线程中断-interrupt

一直以为执行了interrupt方法就可以让线程结束,并抛出InterruptedException. 今天看了Java并发编程实战的第七章发现并不是这么回事,在这章的开头就提到

要使任务和线程能安全、快速、可靠地停止下来,并不是一件容易的事。Java没有提供任何机制来安全地终止线程。但它提供了(Interruption),这是一种协作机制,能够使一个线程终止另一个线程的当前工作

如上提到的是协作,而不是强制。因为如果需要被中断的线程任务实现没有准守这样的协作约定,那么其他线程就没有办法通过interrupt去中断它。比如下面的foreverThread就没有对interrupt设置的boolean变量进行检测。

        Thread foreverThread = new Thread(new Runnable() {
            @Override
            public void run() {
                int t = 0;
                for (int i = 0; i < 1; i++) {
                    i--;
                    if (t++ % 10000 == 0) {
                        System.out.println(".");
                    }
                }
            }
        });
        foreverThread.start();
        foreverThread.interrupt();

如书中所述,每个线程都有一个boolean类型的中断状态,我们可以通过调用线程的interrupt方法来设置这个值为true,并通过isInterrupted方法来检测这个值是否被置为true。这样当一个线程(比如主线程)通过调用另外一个(比如任务)线程interrupt方法,然后这个任务线程内有检测该boolean值是否置位的逻辑,如果为true这中止当前的工作,然后按照库方法那样抛出一个InterruptException来提示当前任务执行被中断了。因为interrupt设置这个boolean变量是线程体相关的,一次设置后它会一直存在于线程体,这样如果不把它重新置为false,那么执行后续其他会检测这个变量的操作就会立即退出抛出InterruptException。出于这样的原因,当检测到这个boolean标记被设为true后,在要抛出InterruptException之前,还要通过调用Thread.interrupted()方法来重置这个boolean标记(即设为false)以免影响后续的操作(这也表示执行这个动作的这部分代码处理了这次中断请求,至于怎么处理,一般就是上文所说的抛出InterruptException)。

阻塞库方法,例如Thread.sleep和Object.wait等,都会检测线程何时中断,并且在发现中断时提前返回。它们在响应中断时执行的操作包括:清除中断状态,抛出InterruptException,表示阻塞操作由于中断而提前结束。

线程实例的interrupt方法用于设置boolean标记,静态的Thread.interrupted方法用于清除boolean标记(即置为false,同时返回以前的boolean值)下面是一个实例:

        Thread iThread = Thread.currentThread();

        iThread.interrupt();

        System.out.println(iThread.isInterrupted());

        System.out.println(Thread.interrupted());

        System.out.println(iThread.isInterrupted());

输出如下:

true
true
false

使用静态的interrupted时应该小心,因为它会清除当前线程的中断状态。如果在调用interrupted时返回了true,那么除非你想屏蔽这个中断,否则必须对它进行处理——可以抛出InterruptedException,或者通过再次调用interrupt来恢复中断

如果我们在检测到interrupt中断后,抛出异常却不调用interrupted清除标记,那么会影响到以后的其他可中断调用:

        BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();

        Thread iThread = Thread.currentThread();

        iThread.interrupt();

        System.out.println(iThread.isInterrupted());

        try {
            queue.take();
        } catch (InterruptedException e) {
            System.out.println("thread is interrupted, isInterrupted:" + iThread.isInterrupted());
        }

输出:

true
thread is interrupted, isInterrupted:false

运行以上代码片段,程序会立即结束而不会再queue.take()上等待。在库函数抛出InterruptException后,通过isInterrupted检测可以发现中断boolean标记已经被清除(置为false)

时间: 2024-10-11 16:38:19

Java 并发:线程中断-interrupt的相关文章

java并发-线程

前言 近来时间比较充裕,正好又看了一遍<实战java高并发程序设计>,故而对java并发一些知识进行下总结,算是温故而知新吧. 一,线程基础 1,新建线程 一般有两种实现方式实现Runnable接口或继承Thread类(Thread类本身也是实现Runnable接口) public class Test { public static void main(String[] args) throws Exception { Thread t1=new TestThread(); Thread t

Java 并发 线程同步

Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取,这将产生同步问题(可见性和同步性的丢失) 比如两个线程同时执行指令account[to] += amount,这不是原子操作,可能被处理如下: a)将account[to]加载到寄存器 b)增加amount c)将结果写回acco

Java 并发 线程属性

Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线程A里,创建另一个线程B的时候,那么A是父线程,B是子线程.当在一个运行的线程A里,创建线程B,然后又创建了线程C,这时候虽然B比C创建早,可是B并不是C的父线程,而A是B和C的父线程. 3.线程的优先级高度依赖于系统,当虚拟机依赖于宿主机平台的线程实现机制时,Java线程的优先级被映射到宿主机平台

Java 并发 线程的优先级

Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于某些原因不执行的时候,低优先级的线程才可能被执行 2.两个优先级相同的线程同时等待执行时,那么运行系统会以round-robin的方式选择一个线程执行(即轮询调度,以该算法所定的)(Java的优先级策略是抢占式调度!) 3.被选中的线程可因为一下原因退出,而给其他线程执行的机会: 1) 一个更高优先

Java 并发 线程的生命周期

Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用getState()时显示为Runnable) d)     Blocked 阻塞 i.          I/O阻塞 (不释放锁) I/O操作完成解除阻塞,进入Runnable状态 ii.          同步阻塞(不释放锁) 运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会

java的线程中断

在java中中断线程可以使用interrupt()函数.此函数虽然不能终止线程的运行,但是可以改变线程的状态为true 即:isInterrupted()的值返回为true 注意:当函数调用了已经被阻塞的线程后,被阻塞的线程将会接收到一个InterruptedException异常.即当前线程即可终止. 例如: package TestThread.ThreadSynchronized; public class TestWaitAll { public static void main(Str

Java并发——线程间的等待与通知

前言: 前面讲完了一些并发编程的原理,现在我们要来学习的是线程之间的协作.通俗来说就是,当前线程在某个条件下需要等待,不需要使用太多系统资源.在某个条件下我们需要去唤醒它,分配给它一定的系统资源,让它继续工作.这样能更好的节约资源. 一.Object的wait()与notify() 基本概念: 一个线程因执行目标动作的条件未能满足而被要求暂停就是wait,而一个线程满足执行目标动作的条件之后唤醒被暂停的线程就是notify. 基本模板: synchronized (obj){ //保护条件不成立

java并发-线程饥饿死锁测试

线程饥饿死锁 <Java并发编程实践>中对线程饥饿死锁的解释是这样的:在使用线程池执行任务时,如果任务依赖于其他任务,那么就可能产生死锁问题.在单线程的Executor中,若果一个任务将另一个任务提交到同一个Executor,并且等待这个被提交的任务的结果,那么这必定会导致死锁.第一个任务在工作队列中,并等待第二个任务的结果:而第二个任务则处于等待队列中,等待第一个任务执行完成后被执行.这就是典型的线程饥饿死锁.即使是在多线程的Executor中,如果提交到Executor中的任务之间相互依赖

Java并发——线程池Executor框架

线程池 无限制的创建线程 若采用"为每个任务分配一个线程"的方式会存在一些缺陷,尤其是当需要创建大量线程时: 线程生命周期的开销非常高 资源消耗 稳定性 引入线程池 任务是一组逻辑工作单元,线程则是使任务异步执行的机制.当存在大量并发任务时,创建.销毁线程需要很大的开销,运用线程池可以大大减小开销. Executor框架 说明: Executor 执行器接口,该接口定义执行Runnable任务的方式. ExecutorService 该接口定义提供对Executor的服务. Sched