1. 描述

可异步关闭和中断的Channel。

(1)实现InterruptibleChannel接口的Channel支持异步关闭:如果一个线程IO阻塞在一个可中断的channel,另一个线程可以执行channel的close方法。这将导致阻塞线程收到AsynchronousCloseException异常。

(2)实现InterruptibleChannel接口的Channel支持中断:如果一个线程IO阻塞在一个可中断的Channel,另一个线程可以执行阻塞线程的interrupt方法。这将导致Channel关闭,阻塞线程收到ClosedByInterruptException异常,阻塞线程将是interrupted状态。

(3)如果线程已经中断,然后在Channel执行阻塞IO操作,channel将关闭,线程将立刻收到ClosedInterruptException异常,且中断状态会保持。

(4)只有实现InterruptibleChannel的Channel才支持异步关闭和中断。

2. Interruptible,InterruptibleChannel,AbstractInterruptibleChannel

2.1 API

package sun.nio.ch;
public interface Interruptible { public void interrupt(Thread t); } package java.nio.channels;
public interface InterruptibleChannel
extends Channel
{ // 关闭Channel
// 任何线程在channel上面IO阻塞,都会收到AsynchronousCloseException异常
public void close() throws IOException; } package java.nio.channels.spi;
// 可中断Channel的基本实现
// 这个类封装底层机制去实现Channel异步关闭和中断。一个具体Channel必须在可能导致IO阻塞之前执行begin方法,之后执行end方法;其中为了保证end必须执行,必须放在try{}finally{}
// 如:
// boolean completed = false;
// try {
// begin();
// completed = ...; // 执行IO阻塞操作
// return;
// } finally {
// end(completed);
// }
// completed标示IO操作是否完成,即对调用者是可见的。
public abstract class AbstractInterruptibleChannel
implements Channel, InterruptibleChannel
{ private final Object closeLock = new Object(); // 关闭需要加锁
private volatile boolean open = true; // 是否打开 protected AbstractInterruptibleChannel() { } // 关闭Channel
// 如果Channel已经关闭,这个方法立刻返回;否则标记channel为关闭状态,然后执行implCloseChannel去执行真实的关闭操作。
public final void close() throws IOException {
synchronized (closeLock) {
if (!open)
return;
open = false;
implCloseChannel();
}
} // 这个方法被close调用去执行真实的channel关闭工作。该方法只有在channel还没有关闭的情况下被调用,从来不会被调用多次。
// 此方法的实现必须设定:在调用关闭方法的时候,在此通道上的I/O操作中阻塞的任何其他线程立即返回,要么抛出异常,要么返回正常。
protected abstract void implCloseChannel() throws IOException; public final boolean isOpen() {
return open;
} // -- Interruption machinery -- private Interruptible interruptor;
private volatile Thread interrupted; // 被中断的线程 // 标记可能IO阻塞的开始,这个方法必须和end匹配使用。
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
} // 标记可能IO阻塞的结束,这个方法必须和begin匹配使用。
// completed: IO操作是否完成。
// 如果Channel被异步关闭,抛出AsynchronousCloseException
// 如果阻塞线程被中断,抛出ClosedByInterruptException
protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);
Thread interrupted = this.interrupted;
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException();
}
if (!completed && !open)
throw new AsynchronousCloseException();
} // 设置当前线程实例的blocker字段值为intr。
static void blockedOn(Interruptible intr) { // package-private
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),
intr);
}
}

AbstractInterruptibleChannel定义了调用模式:

boolean completed = false;
try {
begin();
completed = ...; // Perform blocking I/O operation
return ...; // Return result
} finally {
end(completed);

(1)ClosedByInterruptException

怎么实现线程中断时关闭channel呢?不能指望调用者调用thread.interrupt()后,在调用Channel.close()

能够想到的办法就是将channel的close方法放到thread.interrupt()方法中,JDK即使这么做的,下面会对Thread的interrupt方法做介绍。  

(2)AsynchronousCloseException

当一个线程阻塞在channel上,另一个线程调用channel的close方法,怎么实现阻塞线程抛出异常呢?难点在channle的close方法必须通知阻塞的线程,让它中断。

具体可以看看sun.nio.ch.ServerSocketChannelImpl里面的implCloseSelectableChannel实现。  

2.2 类图

2.3 AbstractInterruptibleChannel源码分析

首先看看Thread类的interrupt方法实现:

class Thread {
// 该线程在可中断的I/O操作中被阻塞的对象,如果有的话。
// blocker的interrupt方法在设置线程中断状态之后必须被执行。
private volatile Interruptible blocker;
private final Object blockerLock = new Object(); void blockedOn(Interruptible b) { // 设置blocker字段
synchronized (blockerLock) {
blocker = b;
}
}
public void interrupt() {
if (this != Thread.currentThread())
checkAccess(); synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this); // 回调blocker的interrupt方法
return;
}
}
interrupt0();
} }  

2.3.1 begin

下面看看AbstractInterruptibleChannel的begin方法:

protected final void begin() {
if (interruptor == null) { // 初始化Interruptible对象
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
interrupted = target;
try { // 关闭可中断的Channel
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
// 设置当前线程实例的blocker字段值为interruptor,保证在调用当前线程的interrupt方法时,可以回调前面初始化Interruptible对象的interrupt方法,从而关闭可中断Channel
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}  

补充一点:AbstractInterruptibleChannel的blockedOn(Interruptible intr)方法的实现:

static void blockedOn(Interruptible intr) {         // package-private
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),
intr);
}  

JavaLangAccess是接口,里面有方法blockedOn

/** Set thread's blocker field. */
void blockedOn(Thread t, Interruptible b);  

而在System.setJavaLangAccess()方法中有个JavaLangAccess的匿名内部类实现,其中blockedOn方法的实现如下:

public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b); // 调用Thread类的方法设置blocker
}

2.3.2 end

protected final void end(boolean completed)
throws AsynchronousCloseException
{
blockedOn(null);// 清空线程的blocker字段
// 如果线程被中断,则在begin里面初始化的Interruptible对象的interrupt方法里面设置了interrupted变量为被中断的线程。
Thread interrupted = this.interrupted;
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException(); // 如果被中断,则抛出ClosedByInterruptException异常
}
if (!completed && !open) // 如果被close,则抛出AsynchronousCloseException异常
throw new AsynchronousCloseException();
}

3. 参考资料

1. JDK1.8.0_111

2. http://jnullpointer.iteye.com/blog/2119982  

JavaNIO - AbstractInterruptibleChannel的更多相关文章

  1. javaNIO(转载)

    (一) Java NIO 概述 Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Chan ...

  2. JAVANIO通道

    package com.nio.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import j ...

  3. javaNIO核心概念

    在java的阻塞IO中使用InputStream和outputStream来进行输入和输出,那么两种流是相互独立使用的,而且每次数据传输都要通过“用户态数据”向“os内核态数据”copy或从“os内核 ...

  4. javaNIO学习

    Buffer其实就是是一个容器对象,它包含一些要写入或者刚读出的数据.在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别.在面向流的I/O中,您将数据直接写入或者将数据直接读到Stre ...

  5. javaNIO是什么?由那几部分组成?各部分的作用。

    Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Sel ...

  6. JavaIO和JavaNIO

    BIO和NIO BIO在之前的服务器处理模型中,在调用ServerSocket.accept()方法时,会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会accept一个新连接,接着启 ...

  7. JavaNIO之Channel

    Channel的本质是通道,用来连接JVM之外数据向JVM内传输数据,比如来自于硬盘的文件,来自于网络的数据包.JVM之外的数据就是通过Channel进行数据传输:如果把Channel比作河道,那么作 ...

  8. (基础篇 走进javaNIO)第二章-NIO入门

    在本章巾,我们会分别对 JDK 的BIO ,NIO 和JDK 1.7 最新提供的 NI02.0的使用进行详细说明 ,通过流程图和代 码讲解,让大 家体会到随着 Ja va 1/0 类库的 不断发展和改 ...

  9. (基础篇 走进javaNIO)第一章-java的i/o演进之路

    Java 是由 SUN公司在 1995 年首先发布 的编程语 言和计算平 台.这基础技术 支持最新 的程序 ,包括 实用程序 .游 戏和业 务应用程序 .J ava 在世界各地 的 8.5  亿 多 ...

随机推荐

  1. 如何在form初始化时自动隐藏FOLDER列

    方法1:直接设定PROMPT列和数据列ITEM的VISIBLE属性为No 方法2:在WHEN-NEW-FORM-INSTANCE触发器里: l_old_itm := :system.cursor_it ...

  2. iOS -- DES算法

    算法步骤: DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥也是64位(实际用到了56位,第8.16.24.32.40.48.56.64位是校验位, 使得每个密钥都有奇数个1),其 ...

  3. Sata win7 热插拔(AHCI)

    主板支持AHCI,把sata模式改成AHCI,在bios打开SATA热插拔开关 开启AHCI,需要修改注册表:HKEY_LOCAL_MACHINE\System\CurrentControlSet\S ...

  4. Nao 类人机器人,Aldebaran Robotics公司

    Nao 类人机器人,Aldebaran Robotics 公司,被SOFTBANK 收购,阿里巴巴.富士康参股. https://www.aldebaran.com/en   一家法国的公司. htt ...

  5. zxing生成二维码和读取二维码

    当然,首先要导入zxing的jar包. 生成二维码代码: package com.imooc.zxing; import java.io.File; import java.nio.file.Path ...

  6. 基于Python的交互式访问

    应用迁移中遇到一些有特殊要求的应用,比如需要通过交互生成一些新的config文件,然后启动应用需要依赖于这些文件,这样在构建镜像的时候基本上是没有办法把这些文件固定的,因为他需要根据运行环境去进行动态 ...

  7. 万里长征第二步——django个人博客(第四步 ——创建数据库)

    在models.py内设置数据库模型 # -*- coding=utf-8 -*- from __future__ import unicode_literals from django.db imp ...

  8. 19个三维GIS软件对比

    19个三维GIS软件对比 麦豆科研技术中心 days ago 我国GIS经过三十多年的发展,理论和技术日趋成熟,在传统二维GIS已不能满足应用需求的情况下,三维GIS应运而生,并成为GIS的重要发展方 ...

  9. RawCap抓取本地回环接口数据包

    RawCap.exe --help ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 D: ...

  10. php 基于cookie的sessIon机制

    session_start()是session机制的开始,它有一定概率开启垃圾回收,因为session是存放在文件中,PHP自身的垃圾回收是无效的,SESSION的回收是要删文件的,这个概率是根据ph ...