设为首页 加入收藏

TOP

JDK源码阅读:InterruptibleChannel与可中断IO(一)
2018-08-09 09:13:14 】 浏览:40
Tags:JDK 源码 阅读 InterruptibleChannel 中断

Java传统IO是不支持中断的,所以如果代码在read/write等操作阻塞的话,是无法被中断的。这就无法和Thead的interrupt模型配合使用了。JavaNIO众多的升级点中就包含了IO操作对中断的支持。InterruptiableChannel表示支持中断的Channel。我们常用的FileChannel,SocketChannel,DatagramChannel都实现了这个接口。

InterruptibleChannel接口

public interface InterruptibleChannel extends Channel
{

    /**
     * 关闭当前Channel
     *     
     * 任何当前阻塞在当前channel执行的IO操作上的线程,都会收到一个AsynchronousCloseException异常
     */
    public void close() throws IOException;
}

InterruptibleChannel接口没有定义任何方法,其中的close方法是父接口就有的,这里只是添加了额外的注释。

AbstractInterruptibleChannel实现了InterruptibleChannel接口,并提供了实现可中断IO机制的重要的方法,比如begin()end()

在解读这些方法的代码前,先了解一下NIO中,支持中断的Channel代码是如何编写的。

第一个要求是要正确使用begin()end()方法:

boolean completed = false;
try {
    begin();
    completed = ...;    // 执行阻塞IO操作
    return ...;         // 返回结果
} finally {
    end(completed);
}

NIO规定了,在阻塞IO的语句前后,需要调用begin()end()方法,为了保证end()方法一定被调用,要求放在finally语句块中。

第二个要求是Channel需要实现java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel这个方法。AbstractInterruptibleChannel在处理中断时,会调用这个方法,使用Channel的具体实现来关闭Channel。

接下来我们具体看一下begin()end()方法是如何实现的。

begin方法

// 保存中断处理对象实例
private Interruptible interruptor;
// 保存被中断线程实例
private volatile Thread interrupted;

protected final void begin() {
    // 初始化中断处理对象,中断处理对象提供了中断处理回调
    // 中断处理回调登记被中断的线程,然后调用implCloseChannel方法,关闭Channel
    if (interruptor == null) {
        interruptor = new Interruptible() {
            public void interrupt(Thread target) {
                synchronized (closeLock) {
                    // 如果当前Channel已经关闭,则直接返回
                    if (!open)
                        return;

                    // 设置标志位,同时登记被中断的线程
                    open = false;
                    interrupted = target;
                    try {
                        // 调用具体的Channel实现关闭Channel
                        AbstractInterruptibleChannel.this.implCloseChannel();
                    } catch (IOException x) { }
                }
            }};
    }
    // 登记中断处理对象到当前线程
    blockedOn(interruptor);

    // 判断当前线程是否已经被中断,如果已经被中断,可能登记的中断处理对象没有被执行,这里手动执行一下
    Thread me = Thread.currentThread();
    if (me.isInterrupted())
        interruptor.interrupt(me);
}

begin()方法中,我们可以看出NIO实现可中断IO操作的思路,是在Thread的中断逻辑中,挂载自定义的中断处理对象,这样Thread对象在被中断时,会执行中断处理对象中的回调,这个回调中,执行关闭Channel的操作。这样就实现了Channel对线程中断的响应了。

接下来重点就是研究“Thread添加中断处理逻辑”这个机制是如何实现的了,是通过blockedOn方法实现的:

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

blockedOn方法使用的是JavaLangAccessblockedOn方法。

SharedSecrets是一个神奇而糟糕的类,为啥说是糟糕呢,因为这个方法的存在,就是为了访问JDK类库中一些因为类作用域限制而外部无法访问的类或者方法。JDK很多类与方法是私有或者包级别私有的,外部是无法访问的,但是JDK在本身实现的时候又存在互相依赖的情况,所以为了外部可以不依赖反射访问这些类或者方法,在sun包下,存在这么一个类,提供了各种超越限制的方法。

SharedSecrets.getJavaLangAccess()方法返回JavaLangAccess对象。JavaLangAccess对象就和名称所说的一样,提供了java.lang包下一些非公开的方法的访问。这个类在System初始化时被构造:

// java.lang.System#setJavaLangAccess
private static void setJavaLangAccess() {
    sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){
        public void blockedOn(Thread t, Interruptible b) {
            t.blockedOn(b);
        }
        //...
    });
}

可以看出,sun.misc.JavaLangAccess#blockedOn保证的就是java.lang.Thread#blockedOn这个包级别私有的方法:

/* The object in which this thread is blocked in an interruptible I/O
 * operation, if any.  The blocker's interrupt method should be invoked
 * after setting this thread's interrupt status.
 */
private volatile Interruptible blocker;
private final Object blockerLock =
编程开发网
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JDK源码阅读:ByteBuffer 下一篇Java连接HBase(kerberized集群)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目