`
hadix
  • 浏览: 25724 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java线程中断

    博客分类:
  • java
阅读更多

Java是一门原生支持多线程的语言,要开启一个线程很容易,使用如下代码

new Thread(new Runnable() {
    @Override
    public void run() {
        //do something
    }
}).start();

 这是我能想到的启动线程的最简单的代码,语义明确.但是要优雅得关闭该线程通常却很难.

 

API中有Thread.stop()这个方法.但是由于各种原因该方法已经被标记为Deprecated所以一定不要使用该方法停止线程运行.正确的方法是使用Thread.isInterrupted()Thread.interrupt(),组合使用这两个方法来中断一个线程.我对这两个方法的理解是,前者检查一个中断标记,后者设置一个中断标记.程序通过在合适的时机检查中断标记来合理的停止任务的执行.这两个方法除了对中断标记做读写之外不影响程序的正常运行,也就是说,无论如何调用interrupt方法,程序始终在自己的控制中.另外还有一个方法interrupted这个方法跟isInterrupted得用相同都是检查中断标记,但它还有另外一个副作用,检查标记后会重置标记为false.就是说连续调用两次该方法第二次调用一定会返回false.

 

下面看一个run方法的实现

@Override
public void run() {
    while (true) {
        boolean shouldStop = doSomething();//执行具体的任务,并返回是否应当停止运行
        if (shouldStop) break;
    }
}

 对于如此实现run方法的Runnable可以不行可任何特别的处理,按正常逻辑执行完,run方法会自然返回,线程也就终止运行了.但是如果想在线程外强制停止线程运行呢?例如有如下的main方法.

 

public static void main(String[] args) throws InterruptedException {

    Thread task = new Thread(new Runnable() {
        @Override
        public void run() {
            while (Thread.currentThread().isInterrupted()) {
                boolean shouldStop = doSomething();
                if (shouldStop) break;
            }
        }
    });
    task.start();

    Thread.sleep(5000);
    //睡眠一段时间后在此位置
    task.interrupt();
}

通过设置循环条件为检查当前线程的中断标记,来控制是否跳出循环,此时每次执行完doSomething都会检查一次中断标记.由于程序在主线程睡眠5秒后会设置task的中断标记为true,所以线程会在5秒多一点的时间后停止.

假设Java虚拟机如果不是如此实现,而是在interrupt的时候直接通过系统底层实现强制终止线程,这时doSomething可能正在像硬盘上面写一段数据,还没写完,突然被停掉了,这会导致数据损坏,这种方式是无脑的,所以通过中断标记的机制让程序自己决定是否要退出是理智的.

 

但是如果doSomething中执行了一个会阻塞线程的操作时会怎么样呢?重新审视上面的代码.

public static void main(String[] args) throws InterruptedException {

    Thread task = new Thread(new Runnable() {
        @Override
        public void run() {
            while (Thread.currentThread().isInterrupted()) {//1
                boolean shouldStop = doSomething();//2,阻塞等待键盘输入
                if (shouldStop) break;
            }
        }
    });
    task.start();

    Thread.sleep(5000);
    //睡眠一段时间后在此位置
    task.interrupt();//3
}

可以想想如果doSomething中在阻塞等待一个键盘输入,5秒后线程中断标记被设置为true,也就是程序按1,2,3的顺序执行了代码.由于2导致线程阻塞,除非有一个键盘输入,否则线程将一直被doSomething阻塞,检查中断标记的语句1得不到执行,此时上面的机制失效.下面具体分析阻塞线程的中断问题.

 

对于线程中的阻塞操作可以分为两种

1.可中断阻塞

这类阻塞操作一般会在线程中断标记被设置为true的时候,抛出异常以停止阻塞,看Thread.sleep()的方法声明.

public static void sleep(long millis, int nanos) throws InterruptedException

 从该声明中可以看到,该方法在其他线程设置该线程的中断标记为true时,会抛出该异常,抛出该异常后会清除当前的中断标记(重置为false)

 

JSE中除了Thread.sleep有许多这样的方法声明.例如LinkedBlockingQueue中的take,put方法.

对于此类中断操作可以做如下代码中的处理

 

public static void main(String[] args) throws InterruptedException {

    Thread task = new Thread(new Runnable() {
        @Override
        public void run() {
            while (Thread.currentThread().isInterrupted()) {
                try {
                    //假设doSomething是一个会抛出InterruptedException的阻塞操作
                    boolean shoudStop = doSomething();
                    if (shoudStop) break;
                } catch (InterruptedException e) {
                    //由于抛出该异常会重置中断标记
                    //所以捕获该异常后需要重新设置中断标记以中断线程
                    Thread.currentThread().interrupt();
                }
            }
        }
    });
    task.start();

    Thread.sleep(5000);
    //睡眠一段时间后在此位置
    //导致doSomething抛出InterruptedException
    task.interrupt();
}

 

 

2.不可中断的阻塞操作

 这类操作对中断标记的设置毫无反应,会顽固的阻塞下去.此类异常一般为IO操作,对于这样的IO阻塞操作一般会在底层资源被关闭(释放)时抛出IOException.例如InputStream的read操作.参考如下代码的处理.

public static void main(String[] args) throws Exception {

    Thread task = new Thread(new Runnable() {
        @Override
        public void run() {
            while (Thread.currentThread().isInterrupted()) {
                try {
                    //阻塞等待标准输入中读取一个字节
                    int data = System.in.read();
                    if (data == -1) break;
                } catch (IOException e) {
                    e.printStackTrace();
                    //由于抛出该异常不会重置中断标记
                    //所以捕获该异常后不许需要重新设置中断标记
                    //直接退出循环即可
                    break;
                }
            }
        }
    });
    task.start();

    Thread.sleep(5000);
    //睡眠一段时间后在此位置
    task.interrupt();
    //关闭(释放)标准输入流,导致System.in.read()抛出IOException
    System.in.close();
}

 看起来也不难,但是实际情况通常比较负责,特别是有些IO操作的close方法本身就有可能阻塞.可以看一下BufferedReader的源代码.

 

 

public class BufferedReader extends Reader {
    ........//省略部分代码
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            for (;;) {
                if (nextChar >= nChars) {
                    fill();
                    if (nextChar >= nChars)
                        return -1;
                }
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == '\n') {
                        nextChar++;
                        continue;
                    }
                }
                return cb[nextChar++];
            }
        }
    }

    public void close() throws IOException {
        synchronized (lock) {
            if (in == null)
                return;
            in.close();
            in = null;
            cb = null;
        }
    }
}

 可以看到read方法和close方法都需要对lock加锁,进行同步.如果线程整阻塞在read操作上,此时read持有lock锁.其他线程调用close时由于无法获取lock锁,也将阻塞,导致死锁.考虑如下代码

public static void main(String[] args) throws Exception {

    final BufferedReader reader 
              = new BufferedReader(new InputStreamReader(System.in));
    Thread task = new Thread(new Runnable() {
        @Override
        public void run() {
            while (Thread.currentThread().isInterrupted()) {
                try {
                    //阻塞等待标准输入中读取一个字节
                    int data = reader.read();
                    if (data == -1) break;
                } catch (IOException e) {
                    e.printStackTrace();
                    //由于抛出该异常不会重置中断标记
                    //所以捕获该异常后不许需要重新设置中断标记
                    //直接退出循环即可
                    break;
                }
            }
        }
    });
    task.start();

    Thread.sleep(5000);
    //睡眠一段时间后在此位置
    task.interrupt();
    //关闭(释放)reader,死锁.close无法执行完成.
    reader.close();
    //直接调用底层资源的close方法即可使阻塞的read操作抛出IOException
    //所以此处应替换为如下注释中的代码
    //System.in.close();
}
分享到:
评论

相关推荐

    Java线程中断示例程序的代码清单.pdf

    Java线程中断是一个关键特性,它允许程序员在运行时通知一个线程停止其当前的工作并进行清理。在上述的Java线程中断示例程序中,我们看到一个简单的场景,模拟了一个班级中的学生(student线程)和教师(teacher线程...

    java线程中断之interrupt和stop.docx

    ### Java线程中断机制详解:`interrupt`与`stop`方法 #### 一、引言 在Java多线程编程中,线程控制是至关重要的技术之一。有时我们需要在特定条件下停止某个线程的执行,或者中断正在等待的线程。Java提供了多种...

    详解Java线程中断知识点

    Java线程中断是多线程编程中一个关键的概念,它允许一个线程在运行时被其他线程“通知”停止其当前活动。线程中断不是立即终止线程,而是发送一个中断信号,由目标线程自己决定如何响应这个信号。在Java中,线程中断...

    浅析java线程中断的办法

    以下内容将详细介绍Java线程中断的方法以及相关知识点。 首先,我们需要了解Java中的中断机制是如何工作的。线程中断机制是基于中断标志实现的,当中断线程时,会设置线程的中断状态,线程可以轮询这个状态来决定...

    深入Java线程中断的本质与编程原则的概述

    本文将深入探讨Java线程中断的本质以及编程原则。 首先,理解线程中断的本质。Java线程中断并不意味着强制停止一个线程,而是设置了一个中断标志,这个标志是JVM内部维护的。通过`Thread.interrupt()`方法,我们...

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

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

    JavaThread中断机制共11页.pdf.zip

    Java线程中断机制是Java多线程编程中的一个重要概念,它允许程序在执行过程中通过某种方式通知线程停止其当前的活动,以便进行资源释放或流程控制。在Java中,线程中断主要通过`Thread.interrupt()`方法实现,这个...

    Java线程(第三版)

    书中还可能涉及异常处理和线程中断,`interrupt()`方法用于标记线程中断状态,`isInterrupted()`和`InterruptedException`用于检查和处理中断。中断机制是Java中优雅停止线程的关键。 另外,Java并发工具库(java....

    Java线程详解大全

    Java线程是并发编程的核心部分,它允许程序在同一时间执行多个独立的任务,从而提高系统效率和响应速度。本文将深入探讨Java线程的概念、生命周期、实现方式以及相关的同步机制。 首先,理解线程的基本概念至关重要...

    java线程实例 各种小Demo

    Java线程是多任务编程的重要概念,它允许程序同时执行多个独立的任务,从而提高系统效率和响应速度。在Java中,线程可以分为用户线程和守护线程,前者是程序运行的基础,而后者是在所有用户线程结束时才终止的后台...

    java线程阻塞中断与LockSupport使用介绍

    Java线程阻塞中断是Java并发编程中的一个重要概念,它涉及到线程的生命周期管理以及协作。`Thread.interrupt()` 方法是用于向线程发送中断请求,而`LockSupport` 是Java 5引入的一个低级别的线程同步工具,提供了比`...

    java通过线程控制程序执行超时(新)

    Java的线程提供了中断机制,通过`Thread.interrupt()`和`Thread.isInterrupted()`方法来控制和检查线程中断状态。在长时间运行的任务中,应定期检查中断标志,一旦检测到中断,及时清理资源并退出。 ```java ...

    Java线程.ppt

    Java线程是Java编程中的重要概念,特别是在多核处理器和并发处理中不可或缺。Java线程允许程序在同一时间执行多个不同的任务,从而提高了程序的效率和响应性。在燕山大学信息学院计算机系的课程中,李峰教授讲解了...

    java多线程Demo

    Java线程有10个优先级(MIN_PRIORITY, NORM_PRIORITY, MAX_PRIORITY),默认优先级是NORM_PRIORITY。但是,线程优先级并不保证绝对的执行顺序,操作系统调度策略可能影响实际执行顺序。 7. join()方法: 一个线程...

    Java线程使用教程

    Java线程是Java编程语言中的一个核心概念,它允许程序同时执行多个任务,极大地提高了程序的并发性和效率。本教程将深入探讨Java线程的使用,帮助开发者掌握这一关键技术。 一、线程基础 1. **线程的概念**:线程...

    java线程.rar

    9. **线程中断**:通过`interrupt()`方法设置线程的中断标志,线程可以通过检查`isInterrupted()`或`interrupted()`方法来响应中断请求。 10. **线程Local变量**:`ThreadLocal`类为每个线程提供独立的变量副本,...

    java线程实战手册

    6. **线程中断与停止**:正确地停止线程是一项挑战,Java提供了interrupt()方法来请求线程中断,但需要注意的是,这并不一定能立即停止线程,需要配合中断标志进行检查和处理。 7. **线程池**:Executor框架和...

    JAVA线程学习(源代码)

    本资源"JAVA线程学习(源代码)"提供了关于Java线程的源代码示例,帮助我们深入理解和实践线程的使用。 首先,我们要理解Java中的线程模型。Java线程由`java.lang.Thread`类或`java.util.concurrent.Executor`框架来...

    java线程与并发编程实践

    Java线程与并发编程实践是Java开发者必备的技能之一,特别是在多核处理器和高并发应用环境中,有效地管理和利用线程能极大地提升程序的性能。本书《java线程与并发实践编程》由Jeff Friesen撰写,2017年2月出版,...

Global site tag (gtag.js) - Google Analytics