funnycoding / blog

A Github Issue Blog
22 stars 0 forks source link

《Java并发编程实战》5.基础构建模块 (四)阻塞方法与中断方法 #28

Open funnycoding opened 4 years ago

funnycoding commented 4 years ago

5.4 阻塞方法与中断方法

线程可能会阻塞或暂停执行,原因有多种:

线程阻塞时,它通常被「挂起」,并处于某种『阻塞状态』**(BLOCK、WAITING 或 TIMED_WAITING)。阻塞操作与执行时间很长的普通操作的差别在于 : 被阻塞的线程必须等待某个 不受它控制 的事件发生后 才能继续执行,例如「等待 I/O 操作完成」,「等待某个锁可用」,或者「等待外部计算的结束」。

当某个外部事件发生时,线程被置回 RUNABLE 可运行状态,并可以再次被调度执行。

BlockingQueueputtake 等方法会抛出 受检查异常Checked ExceptionInterruptedException,这与类库中其他一些方法的做法相同,例如 Thread.sleep

当某方法抛出 InterruptedException 时,表示该方法是一个阻塞方法,如果这个方法被中断,那么它将努力提前结束 「阻塞状态」。<---【阻塞方法的定义,比如 Thread.slee

Thread 提供了 interrupt 方法,用于中断线程或者查询线程是否已经被中断。 每个线程都有一个布尔类型的属性,表示线程的中断状态,当中断线程时,将设置这个状态。

中断是一种「协作机制」,一个线程不能强制其他线程停止正在执行的操作而去执行其他的操作。<---【也就是只能建议,而不能强制停止】

当 「线程A」 中断 「线程B」 时,A 仅仅是要求 B 在执行到某个可以暂停的地方停止正在执行的操作 —— 前提是 「线程B」 愿意停止下。虽然在 API 或者 语言规范中并没有为中断定义任何特定应用级别的语义,但最常使用中断的情况就是 「取消某个操作」。

方法对中断请求的响应度越高,就越容易及时取消那些执行时间很长的操作。<---【那么方法的响应度是可以设置的吗? 总是把话说一半。。。】

当在代码中调用了一个将抛出 InterruptedException 异常的方法时,你自己的方法也就变成了一个阻塞方法,并且必须要处理对中断的响应,对于库代码来说,有两种基本选择:

程序清单 5-10 恢复中断状态以屏蔽中断:

// Restoring the interrupted status so as not to swallow the interrupt
// 恢复中断状态以避免中断被屏蔽
public class TaskRunnable implements Runnable {

    BlockingQueue<Task> queue;

    @Override
    public void run() {
        try {
            processTask(queue.take());
        } catch (InterruptedException e) {
            // 恢复被中断的状态 将调用者线程的中断状态设为true。
            Thread.currentThread().interrupt();
        }
    }

    // 对Task进行处理的业务逻辑代码
    void processTask(Task task) {
        // 处理 task
    }

    interface Task {
    }
}

还可以采用一些更复杂的终端处理方法,但是上述两种方法已经可以应付大多数情况了。 然而在出现 InterruptedException不应该做的事情是:捕获它但是不做出任何响应。【也就是异常的侵吞,这种操作在任何时候应该都是不推荐的】。这将使调用栈更上层的代码无法对中断采取处理措施,因为线程被中断的证据已经丢失。

只有在一种特殊的情况下才能屏蔽中断 —— 对 Thread 进行扩展,并且能够调用栈上更高层的代码。 [第7章]() 将进一步介绍 「取消」 和 「中断」 等操作。

脑图