前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NIO 之 Channel

NIO 之 Channel

作者头像
java404
发布2018-05-18 15:08:59
6790
发布2018-05-18 15:08:59
举报
文章被收录于专栏:java 成神之路java 成神之路

可参考之前写过的文章:NIO 之 Channel实现原理

概述

通道( Channel)是 java.nio 的主要创新点。它们既不是一个扩展也不是一项增强,而是全新、极好的 Java I/O 示例,提供与 I/O 服务的直接连接。 Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。

Channel 接口定义

代码语言:javascript
复制
public interface Channel extends Closeable {
    public boolean isOpen();
    public void close() throws IOException;
}

Channel 接口,只抽象了 isOpen() 方法和 close() 方法。

是否感觉很奇怪,为什么没有 open() 方法?

Channel 概述

I/O 分为File I/O 和 Stream I/O。

File I/O 对应的是文件(file)通道。

Stream I/O 对应的是( socket)通道。

FileChannel 类和三个 socket 通道类: SocketChannel、 ServerSocketChannel 和 DatagramChannel。

通道可以以多种方式创建。 Socket 通道有可以直接创建新 socket 通道的工厂方法。但是一个FileChannel 对象却只能通过在一个打开的 RandomAccessFile、 FileInputStream 或 FileOutputStream对象上调用 getChannel( )方法来获取。您不能直接创建一个 FileChannel 对象。(这也是 Channel 接口没有定义 open() 方法的原因)。

ByteChannel

通过源码发现每一个 file 或 socket 通道都实现ByteChannel。

ByteChannel
代码语言:javascript
复制
public interface ByteChannel extends ReadableByteChannel, WritableByteChannel { }
ReadableByteChannel
代码语言:javascript
复制
public interface ReadableByteChannel extends Channel {
    public int read(ByteBuffer dst) throws IOException;
}
WritableByteChannel
代码语言:javascript
复制
public interface WritableByteChannel extends Channel{
    public int write(ByteBuffer src) throws IOException;
}

通道可以是单向或者双向的。实现这两种接口其中之一的类都是单向的,只能在一个方向上传输数据。如果一个类同时实现这两个接口,那么它是双向的,可以双向传输数据。

每一个 file 或 socket 通道都实现全部三个接口。从类定义的角度而言,这意味着全部 file 和 socket 通道对象都是双向的。这对于 sockets 不是问题,因为它们一直都是双向的,不过对于 files 却是个问题了。

一个文件可以在不同的时候以不同的权限打开。从 FileInputStream 对象的getChannel( )方法获取的 FileChannel 对象是只读的,不过从接口声明的角度来看却是双向的,因为

FileChannel 实现 ByteChannel 接口。在这样一个通道上调用 write( )方法将抛出未经检查的NonWritableChannelException 异常,因为 FileInputStream 对象总是以 read-only 的权限打开文件。一个连接到只读文件的 Channel 实例不能进行写操作,即使该实例所属的类可能有 write( )方法。基于此,程序员需要知道通道是如何打开的,避免试图尝试一个底层 I/O服务不允许的操作。

阻塞非阻塞

通道可以以阻塞( blocking)或非阻塞( nonblocking)模式运行。非阻塞模式的通道永远不会让调用的线程休眠。请求的操作要么立即完成,要么返回一个结果表明未进行任何操作。只有面向流的( stream-oriented)的通道,如 sockets 和 pipes 才能使用非阻塞模式。file 通道是不能以非阻塞的模式运行。

Channel.close()

与缓冲区(Buffer)不同,通道(Channel)不能被重复使用。一个打开的通道即代表与一个特定 I/O 服务的特定连接并封装该连接的状态。当通道关闭时,那个连接会丢失,然后通道将不再连接任何东西。

调用通道的close( )方法时,可能会导致在通道关闭底层I/O服务的过程中线程暂时阻塞,哪怕该通道处于非阻塞模式。通道关闭时的阻塞行为(如果有的话)是高度取决于操作系统或者文件系统的。

源码简略如下:

代码语言:javascript
复制
    //该代码是 FileChannel 的关闭方法 (在FileChannel 的父类 AbstractInterruptibleChannel 中)
    public final void close() throws IOException {
        synchronized (closeLock) {
            if (!open)
                return;
            open = false;
            implCloseChannel();
        }
    }

从上面代码中可以分析出在一个通道上多次调用close( )方法是没有坏处的,但是如果第一个线程在close( )方法中阻塞(使用synchronized 锁),那么在它完成关闭通道之前,任何其他调用close( )方法都会阻塞。后续在该已关闭的通道上调用close( )不会产生任何操作,只会立即返回。

Channel.isOpen( )

可以通过 isOpen( )方法来测试通道的开放状态。如果返回 true 值,那么该通道可以使用。如果返回 false 值,那么该通道已关闭,不能再被使用。尝试进行任何需要通道处于开放状态作为前提的操作,如读、写等都会导致 ClosedChannelException 异常。

源码简略如下:

代码语言:javascript
复制
//该代码在FileChannel 的子类中实现的
public class FileChannelImpl  extends FileChannel{

    public int read(ByteBuffer dst) throws IOException {
        ensureOpen();
        ......
    }

    public int write(ByteBuffer src) throws IOException {
        ensureOpen();
        ......
    }
    private void ensureOpen() throws IOException {
        if (!isOpen())
            throw new ClosedChannelException();
    }
}

通道响应 Interrupt 中断

通道引入了一些与关闭和中断有关的新行为。通道实现 InterruptibleChannel 接口。

如果一个线程在一个通道上被阻塞并且同时被中断(由调用该被阻塞线程的 interrupt( )方法的另一个线程中断),那么该通道将被关闭,该被阻塞线程也会产生一个 ClosedByInterruptException 异常。

源码简略如下:

代码语言:javascript
复制
public class FileChannelImpl extends FileChannel{

    public int write(ByteBuffer src) throws IOException {
       ......
       end(n > 0);
       ......
    }

    public int read(ByteBuffer dst) throws IOException {
       ......
       end(n > 0);
       ......
    }

    public FileLock lock(long position, long size, boolean shared)  throws IOException {
       ......
       end(n > 0);
       ......
    }
    .......
}
代码语言:javascript
复制
public abstract class AbstractInterruptibleChannel
    implements Channel, InterruptibleChannel {

    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();
    }
    ......
}

Interrupt 关闭 Channel 实例

代码语言:javascript
复制
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelInterruptDemo {

    public static void main(String[] args) throws Exception  {
        FileChannel fc = new RandomAccessFile(new File("d:/a.txt"), "rw").getChannel();
        System.out.println("Channel isOpen : " + fc.isOpen());
        
        Thread t = new Thread(new Task(fc));
        t.start();
        t.interrupt();
        t.join();
        
        System.out.println("Channel isOpen : " + fc.isOpen());
        
        fc.close();
    }
}
class Task implements Runnable{
    FileChannel fc;
    
    public Task(FileChannel fc) {
        this.fc = fc;
    }

    @Override
    public void run() {
        try {
            fc.position(Integer.MAX_VALUE/2);
            fc.write(ByteBuffer.wrap("hello".getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

输出结果

从结果中发现,只要Channel 所在的线程 interrupt 就会自动关闭channel。


本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.05.05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • Channel 接口定义
  • Channel 概述
    • ByteChannel
      • ByteChannel
        • ReadableByteChannel
          • WritableByteChannel
          • 阻塞非阻塞
          • Channel.close()
            • Channel.isOpen( )
            • 通道响应 Interrupt 中断
            • Interrupt 关闭 Channel 实例
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档