NIO 四 通道接口

一 通道

  三章节的缓冲区介绍完毕后就开始进入下一个主题——通道。如果说缓冲区仅仅是客户端操作缓冲数据的港口,那么船舶和航线就是通道,通道负责将货物(缓冲数据)从两个港口间传递,已达港口的货物或许会被消费到各个商店,或许会被押到船舶上送往下一个目的地。

  单单讲缓冲区是没有意义的(哼,我不信,包装数组就当是扩充API使用不行么……),NIO需要解决的是数据如何在不同客户端间流动,这是一个双向问题,因此NIO在设计通道时,复杂度远高于缓冲区设计。

  但实际上真正应用到项目中的通道又十分有限,所以通道部分我会拆成两个部分,一个是通道的接口设计,也就是本章要介绍的内容;另一个是NIO已实现的通道类型,并且做示意性的使用说明介绍。

二 通道接口

  之所以先介绍接口,是因为NIO对通道的设计进行了非常多的功能抽象,具体可用的通道类具备哪些特性都是由其实现的接口决定的,所以要想比较透彻的了解通道设计,必须先弄清楚整个NIO通道体系的接口设计。需要注意的是,虽然通道接口定义了实现类的行为,但是其行为的特性如何,还是要依具体实现决定,这部分内容在后续介绍可用通道类的时候再结合源码设计来说。

  整个NIO通道接口派生关系如下:

NIO通道接口UML

三 接口说明

  这部分不会把接口的设计说明的太过详细,实际上从接口的定义上并不能很明显的看出接口方法的具体行为,JDK也仅仅是在接口方法的注释上给了一些实现类需要注意的点,我查了一些资料,绝大部分的书和网文也都是对这些接口方法的注释翻译了一下……

3.1 AutoCloseable

  这个接口是JKD1.7引入的,用来支持try-catch-resources语法,解决finally块中关闭异常时的异常覆盖问题。这意味这所有Channel类均具备自动关闭的特性。

  注意咯,我特意提到了一句话:finally块中关闭异常时的异常覆盖!这里很多刚入门的朋友可能比较难理解,我具一个例子:

public class ExceptionTest implements AutoCloseable {
    public static void main(String[] args) throws Exception {
        ExceptionTest exceptionTest = null;
        try {
            exceptionTest = new ExceptionTest();
            // 注意open会抛出异常
            exceptionTest.open();
        } finally {
            try {
                if (exceptionTest != null) {
                    // open异常后执行close,close继续抛出异常
                    exceptionTest.close();
                }
            } catch (Exception e) {
                // open的异常就丢失了
                e.printStackTrace();
            }
        }
    }


    public void open() throws IOException {
        System.out.println("invoke open method");
        throw new IOException("open method exception");
    }

    public void close() throws Exception {
        System.out.println("invoke close method");
        throw new IOException("close method exception");
    }
}

输出结果如下:
invoke open method
invoke close method
java.io.IOException: close method exception
	at com.demo.nio.ExceptionTest.close(ExceptionTest.java:36)
	at com.demo.nio.ExceptionTest.main(ExceptionTest.java:19)
Exception in thread "main" java.io.IOException: open method exception
	at com.demo.nio.ExceptionTest.open(ExceptionTest.java:31)
	at com.demo.nio.ExceptionTest.main(ExceptionTest.java:14)

  上面的示例程序你一定不陌生,JDBC处理连接关闭的时候常常会用到上面的写法,实际上当关闭SQL连接时未必不会出现其他异常,那么在finally中关闭资源时经常会丢失最初的异常信息,而try-catch-resource语法可以规避这个问题:

public class ExceptionTest implements AutoCloseable {
    public static void main(String[] args) throws Exception {
        try (ExceptionTest exceptionTest = new ExceptionTest()) {
            exceptionTest.open();
        }
    }


    public void open() throws IOException {
        System.out.println("invoke open method");
        throw new IOException("open method exception");
    }

    public void close() throws Exception {
        System.out.println("invoke close method");
        throw new IOException("close method exception");
    }
}

输出结果如下:
invoke open method
invoke close method
Exception in thread "main" java.io.IOException: open method exception
	at com.demo.nio.ExceptionTest.open(ExceptionTest.java:31)
	at com.demo.nio.ExceptionTest.main(ExceptionTest.java:8)
	Suppressed: java.io.IOException: close method exception
		at com.demo.nio.ExceptionTest.close(ExceptionTest.java:36)
		at com.demo.nio.ExceptionTest.main(ExceptionTest.java:9)

  从异常堆栈信息的对比很容易能看出来,使用try-catch-resource的方式不会丢失任何异常信息,并且这种语法也极大的化简了编码工作量。

3.2 Closeable

  这是一个非常古老的接口,用于支持可显式关闭的资源,JDK1.5版本加入,1.7版本扩展了父接口AutoCloseable。

  这意味着所有的通道都支持自动关闭和显式关闭。

3.3 Channel

  这是真正意义上的通道接口,因为通道必须支持关闭操作,那么客户端就必须时刻掌握当前时刻通道是否可用,因此Channel接口中提供了isOpen函数。

2.4 InterruptibleChannel

  这个接口什么方法都没留下,仅仅留下一个继承而来的close方法,然后补充了一些注释内容,从注释内容上看InterruptibleChannel表示一个可中断的通道,如果应用线程阻塞在此类型的通道上,那么当通道关闭时,会收到java.nio.channels.AsynchronousCloseException异常,以此来支持Java的中断模型。

  实际上接口的实现远比接口的定义复杂:

  1. 如果当前线程在实现了此接口的通道上调用了阻塞方法,那么其他线程跳用此通道的close方法的时候,当前线程会收到AsynchronousCloseException;
  2. 如果当前线程在实现了此接口的通道上调用了阻塞方法,那么其他线程跳用此通道的interrupte方法时,通道会被关闭,当前线程也会收到AsynchronousCloseException,并且这个线程会一直处理中断状态,除非状态被重置

2.5 ReadableByteChannel

  提供了从通道读取数据的方法,需要注意的是当前线程读取数据时,其他线程的读操作会被阻塞,并且涉及操作系统底层数据传输,因此仅支持字节读取。

2.6 ScatteringByteChannel

  这个接口作为ReadableByteChannel的直接派生接口,扩展了字节读的操作,允许将通道中的数据读取到多个缓冲区中。

2.7 WritableByteChannel

  和读相对,这是字节写入接口,和字节读相同的是当前线程写入时,其他线程的写操作会被阻塞。

2.8 GatheringByteChannel

  和ScatteringByteChannel类似,允许多个缓冲区向通道中写入数据。

2.9 ByteChannel

  这个接口从前文的接口关系UML图中可以看出,结合了字节读写接口,此接口的实现类可以具备读写操作。

2.10 SeekableByteChannel

  这个接口派生自ByteChannel,在读写操作的基础上,扩充了对position的维护操作。

2.11 AsynchronousChannel

  这是实现异步IO操作的核心接口,并且实现了此接口的通道是线程安全的,允许并发读写,但是需要注意的是不允许在一个IO未完成的情况下再次读写。

2.12 AsynchronousByteChannel

  在AsynchronousChannel接口的基础上扩充了以字节为单位的读写操作。

2.13 NetworkChannel

  有一个非常核心的接口,支持Socket的数据读写操作,将通道中的数据和Socket关联,网络NIO实现的最重要的接口。

2.14 MulticastChannel

  这个接口稍微难理解一些,但是了解IP广播的话就不难了,它的功能就是支持IP多路广播的,将多个IP打包成一组,然后将报文向组内所有IP对应的主机发送。

四 结语

  如果想关注更多硬技能的分享,可以参考积少成多系列传送门,未来每一篇关于硬技能的分享都会在传送门中更新链接。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬睡客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值