这两天重新看了一下java.nio.channels.spi.AbstractInterruptibleChannel的代码,感觉又有一些收获,记录一下。AbstractInterruptibleChannel实现了InterruptibleChannel接口。InterruptibleChannel接口的描述非常重要,直接影响AbstractInterruptibleChannel的行为:
A channel that implements this interface is asynchronously closeable: If a thread is blocked in an I/O operation on an interruptible channel then another thread may invoke the channel's close method. This will cause the blocked thread to receive an AsynchronousCloseException.
A channel that implements this interface is also interruptible: If a thread is blocked in an I/O operation on an interruptible channel then another thread may invoke the blocked thread's interrupt method. This will cause the channel to be closed, the blocked thread to receive a ClosedByInterruptException, and the blocked thread's interrupt status to be set
说明了两种异常情形:
1.如果线程在channel上阻塞,另外一个线程调用了channel的close方法,会导致阻塞在channel上的线程接收到一个AsynchronousCloseException。
2.如果线程在channel上阻塞,另外一个线程调用该线程的interrupt方法,会导致channel关闭,阻塞的线程接收到ClosedByInterruptException。
这实际上定义了channel上的线程的中断、channel关闭之间关系,基本的意思是:
1.阻塞线程中断->channel关闭,抛出ClosedByInterruptException;
2.channel关闭->阻塞线程抛出AsynchronousCloseException;
AbstractInterruptibleChannel定义了调用模式:
boolean completed = false;
try {
begin();
completed = ...; // Perform blocking I/O operation
return ...; // Return result
} finally {
end(completed);
这个模式主要作用就是finally的end方法一定会执行。
1.先看看ClosedByInterruptException的情形
怎么实现线程中断时关闭channel呢?
不能指望调用者调用thread.interrupt()后,紧跟着再调用channel.close()。
能够想到的办法就是将channel的close方法放到thread.interrupt方法中,JDK就是这么做的,我揣测为了实现这个功能,专门修改thread的方法,为此还引入了变量:
private volatile Interruptible blocker
在上面的begin方法中,为当前线程植入了一个中断触发器Interruptible,
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt() {
synchronized (closeLock) {
if (!open)
return;
interrupted = true;
open = false;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
if (Thread.currentThread().isInterrupted())
interruptor.interrupt();
}
这个Interruptible回调AbstractInterruptibleChannel的关闭方法
AbstractInterruptibleChannel.this.implCloseChannel()
当然,Thread的interrupt方法也要修改,当调用thread.interrupt方法时,如果中断触发器不为空,便会调用触发器的方法。
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0();
b.interrupt();
return;
}
}
interrupt0();
}
上面的这几处就实现了线程中断时,会调用channel的close方法。将这两个动作绑定在一起。
在上面的end方法中,会调用blockedOn(null)将中断触发器清空。
protected final void end(boolean completed) throws AsynchronousCloseException{
blockedOn(null);
if (completed) {
interrupted = false;
return;
}
if (interrupted) throw new ClosedByInterruptException();
if (!open) throw new AsynchronousCloseException();
}
整体过程是:
1.线程执行begin方法,植入中断触发器,然后可能在channel上阻塞;
2.另外一个线程调用步骤1线程的interrupt方法;
3.interrupt方法会执行channel的close方法,并设置标志位interrupted = true;
4.线程进入end方法,清空线程的中断触发器,当判断interrupted = true时,抛出ClosedByInterruptException。
2.先看看AsynchronousCloseException的情形
当一个线程阻塞在channel上,另外一个线程调用channel的close方法,怎么实现阻塞线程抛出异常呢?难点在于channel的close方法必须通知阻塞的线程,让它中断。
拔了一下代码,大致理清了过程。
实现类AbstractSelectableChannel的implCloseChannel方法会调用implCloseSelectableChannel方法。这个抽象方法的实现类有多个,比如sun.nio.ch.ServerSocketChannelImpl的implCloseSelectableChannel实现
protected void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
nd.preClose(fd);
long th = thread;
if (th != 0)
NativeThread.signal(th);
if (!isRegistered())
kill();
}
}
可以看到NativeThread.signal执行通知线程中断。thread来自于accept方法
public SocketChannel accept() throws IOException {
...
thread = NativeThread.current();
...
NativeThread.signal是native方法,在NativeThread.c中看到相应的实现:
Java_sun_nio_ch_NativeThread_signal(JNIEnv *env, jclass cl, jlong thread)
{
#ifdef __linux__
if (pthread_kill((pthread_t)thread, INTERRUPT_SIGNAL))
JNU_ThrowIOExceptionWithLastError(env, "Thread signal failed");
#endif
}
俺不懂C,但大致能看出对线程执行中断操作。
整个过程如下:
1.某个线程执行了channel的close方法;
2.channel的会调用操作系统函数对阻塞的线程发出中断指令;
3.阻塞线程执行end方法,此时的interrupted=false,这个值只有执行interruptor.interrupt()方法时才会置为true,所以不会抛出ClosedByInterruptException。
4.此时open = false,抛出AsynchronousCloseException异常。
通过上面的分析,有几点收获
1.如果想在线程中断时执行一些操作,中断触发器是一个很好的钩子;
2.windows平台的windows\classes\sun\nio\ch\NativeThread.java中,current方法和signal方法都是空实现。所以不太明白windows平台上,channel的close方法如何通知阻塞线程中断的。
class NativeThread {
static long current() { return -1; }
static void signal(long nt) { }
}
3.channel的实现类中保存了thread的ID,是不是一个channel某个时间点上只能对应一个线程来操作?
分享到:
相关推荐
Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...
java NIO.zip
找了好久,终于找到了,java刷新同步获取网络资源
Java NIO原理 图文分析及代码实现
今天我们继续就Android DDMS源码一起分析NIO非阻塞通讯方式,Android123也会给大家分享下手机和PC互通中的一些技术
整理:网站建设与网站制作公司-VeiSun.Com)简单介绍:MINA框架是对java的NIO包的一个封装,简化了NIO程序开发的难度
用Java实现非阻塞通信 ,用ServerSocket和Socket来编写服务器程序和客户程序,是Java网络编程的最基本的方式。 httpcore-nio-4.3.jar包
jdk1.6 源码 包含nio等 jdk1.6 源码 包含nio等 jdk1.6 源码 包含nio等
NULL 博文链接:https://goon.iteye.com/blog/1775421
高性能网络编程必备技能之IO与NIO阻塞分析..................
Java NIO与IO性能对比分析.pdf
该包封装过的NIO比sun本身的更容易处理 server中只有区区几行就搞定了: //创建listener TimeHandler timer = new TimeHandler(); //获取Notifier Notifier notifier = Notifier.getNotifier(); //注册监听 notifier....
赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...
定义作为数据容器的缓冲区,并提供其他 NIO 包的概述。 NIO API 的集中抽象为: 缓冲区,它们是数据容器; 字符集 及其相关解码器 和编码器, 它们在字节和 Unicode 字符之间进行转换; 各种类型的通道,...
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...
Java.NIO资源下载资源下载
Android开发进阶之NIO非阻塞包[定义].pdf
NIO入门.chm NIO入门.chm NIO入门.chm
java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...
赠送jar包:xnio-nio-3.8.4.Final.jar; 赠送原API文档:xnio-nio-3.8.4.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.4.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.4.Final.pom; 包含翻译后的API...