Channel

什么是Channel

通道是一个对象,它表示到硬件设备、文件、网络套接字、应用程序组件或另一个能够执行写、读和其他I/O操作的实体的开放连接。
通道有效地在字节缓冲区和基于操作系统的I/O服务源或目的地之间传输数据
通道是访问I/O服务的网关。
通道使用字节缓冲区作为发送和接收数据的endpoint

操作系统文件句柄或文件描述符与通道之间通常存在一对一的对应关系,当你在文件上下文中使用通道时,通道通常会连接到打开的文件描述符。尽管通道比文件描述符更抽象,但它们仍然能够对操作系统的I/O设施建模

java中提供了java.nio.channelsjava.nio.channels.spi来实现channel相关功能

java.nio.channels.Channel接口

所有通道都实现了java.nio.channels.Channel接口,该接口只有两个方法

  • boolean isOpen
    判断channel是否打开
  • void close()
    关闭该通道。当该通道已经关闭时,调用close()不会有任何效果,当另一个线程已经调用close()时,一个新的close()调用将阻塞,直到第一个调用结束,然后close()静默返回。当发现IO错误时会抛出java.io.IOException,对于已经关闭的channel调用IO操作会抛出java.nio.channels.ClosedChannelException

java.nio.channels.WritableByteChannel 接口

  • abstract int write(ByteBuffer buffer)
    将字节序列从缓冲区写入当前通道,该方法返回实际写入的字节数。当不支持向channel写入数据时抛出java.nio.channels.NonWritableChannelException,当channel已经被关闭时抛出java.nio. channels.ClosedChannelException,当在写入过程中另一个线程关闭了channel会抛出java.nio.channels.AsynchronousCloseException,当在写入过程中另一个线程调用了改线程的interinterrupt方法会抛出java.nio.channels.ClosedByInterruptException(从而关闭通道并设置当前线程的中断状态),当发生IO异常时抛出IOException

java.nio.channels.ReadableByteChannel 接口

  • int read(ByteBuffer buffer)
    从当前通道读取字节到缓冲区,这个方法返回实际读取的字节数(或者当没有更多字节可读时返回-1),它抛出的异常同WritableByteChannel

java.nio.channels.ByteChannel

ByteChannel是一个marker interface ,它继承了WritableByteChannel 接口和ReadableByteChannel 接口

java.nio.channels.InterruptibleChannel接口

可以异步关闭和中断的通道

实现此接口的通道是异步关闭的:如果线程阻塞在可中断通道上的I/O操作中上,那么另一个线程可能调用该通道的close方法。这将导致阻塞的线程接收AsynchronousCloseException。

实现此接口的通道也是可中断的:如果一个线程在可中断通道上的I/O操作中被阻塞,那么另一个线程可能调用被阻塞线程的interrupt方法。这将导致通道被关闭,阻塞的线程接收到ClosedByInterruptException,并设置阻塞线程的中断状态。如果线程的中断状态已经设置,并且它调用了通道上的阻塞I/O操作,那么通道将被关闭,线程将立即接收到一个ClosedByInterruptException;它的中断状态将保持设置

通过在表达式中使用instanceof运算符(例如channel instanceof InterruptibleChannel),可以确定通道是否支持异步关闭和中断

java.nio.channels.Channels 工具类

通道实用程序类,它提供了从流中获取通道的两种方法,产生的通道不会被缓冲,它将简单地将其I/O操作重定向到给定的流。关闭通道将导致流被关闭

WritableByteChannel newChannel(OutputStream outputStream)
ReadableByteChannel newChannel(InputStream inputStream)

传统IO类的getChannel()方法

传统IO类也被改进通过getChannel()获得channel

获取Channel

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

public class ChannelDemo {
    public static void main(String[] args) {
        ReadableByteChannel src = Channels.newChannel(System.in);
        WritableByteChannel dest = Channels.newChannel(System.out);
        try {
            copy(src, dest); // or copyAlt(src, dest);
        } catch (IOException ioe) {
            System.err.println("I/O error: " + ioe.getMessage());
        } finally {
            try {
                src.close();
                dest.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }

    static void copy(ReadableByteChannel src, WritableByteChannel dest)
            throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(2048);
        while (src.read(buffer) != -1) {
            buffer.flip();
            dest.write(buffer);
            buffer.compact();
        }
        buffer.flip();
        while (buffer.hasRemaining())
            dest.write(buffer);
    }

    static void copyAlt(ReadableByteChannel src, WritableByteChannel dest)
            throws IOException {
        ByteBuffer buffer = ByteBuffer.allocateDirect(2048);
        while (src.read(buffer) != -1) {
            buffer.flip();
            while (buffer.hasRemaining())
                dest.write(buffer);
            buffer.clear();
        }
    }
}

需要注意的是,单个write()方法调用可能不会写入缓冲区的全部内容。类似地,一个read()调用可能无法完全填充缓冲区

Scatter/Gather I/O

通道提供单个I/O操作跨多个缓冲区执行的能力。这种能力称为分散/聚集I/O
Java 提供了java.nio.channels.ScatteringByteChannel 接口支持scattering和java.nio.channels.GatheringByteChannel支持gathering

  • Scatter(分散) 在读取操作上下文中,通道的内容是分散的(填充)顺序写入多个缓冲区,每个缓冲区都被填满到其limit,直到通道为空或直到使用了全部缓冲区空间
  • Gather(聚集) 在写操作的上下文中,几个缓冲区的内容被依次收集(排干),然后写入通道,这些缓冲区不需要具有相同的容量。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;

public class ChannelDemo {
    public static void main(String[] args) throws IOException {
        ScatteringByteChannel src;
        FileInputStream fis = new FileInputStream("x.dat");
        src = (ScatteringByteChannel) Channels.newChannel(fis);
        ByteBuffer buffer1 = ByteBuffer.allocateDirect(5);
        ByteBuffer buffer2 = ByteBuffer.allocateDirect(3);
        ByteBuffer[] buffers = {buffer1, buffer2};
        src.read(buffers);
        buffer1.flip();
        while (buffer1.hasRemaining())
            System.out.println(buffer1.get());
        System.out.println();
        buffer2.flip();
        while (buffer2.hasRemaining())
            System.out.println(buffer2.get());
        buffer1.rewind();
        buffer2.rewind();
        GatheringByteChannel dest;
        FileOutputStream fos = new FileOutputStream("y.dat");
        dest = (GatheringByteChannel) Channels.newChannel(fos);
        buffers[0] = buffer2;
        buffers[1] = buffer1;
        dest.write(buffers);
    }
}

ScatteringByteChannel提供了以下方法:

  • long read(ByteBuffer[] buffers, int offset,int length)
  • long read(ByteBuffer[] buffers)
    GatheringByteChannel提供了以下方法
  • long write(ByteBuffer[] buffers, int offset,int length)
  • long write(ByteBuffer[] buffers)

File Channels

RandomAccessFile声明了用于返回文件通道实例的FileChannel getChannel()方法,该方法描述了到文件的打开连接。事实证明,FileInputStreamFileOutputStream也提供了相同的方法。相反java.io.FileReaderjava.io.FileWriter没有提供任何获取file channel 的方法

抽象类java.nio.channels。FileChannel描述文件通道。
因为该类实现了InterruptibleChannel接口,所以文件通道是可中断的
因为这个类实现了ByteChannel,GatheringByteChannel和ScatteringByteChannel接口,您可以对底层文件进行写入、读取和执行分散/聚集I/O

与非线程安全的缓冲区不同,文件通道是线程安全

文件通道维护着一个文件读写的当前位置,可以通过文件通道获取和修改当前位置。也可以通过文件通道将缓存数据刷写到磁盘,读取写入文件内容,获取通道关联的文件大小,截取文件,为整个文件或者部分文件区域加锁,执行内存映射文件I/O,直接与另外一个channel传输数据
filechannel 相关方法

  • void force(boolean metadata)
  • long position()
  • FileChannel position(long newPosition)
  • int read(ByteBuffer buffer)
  • int read(ByteBuffer dst, long position)
  • long size()
  • FileChannel truncate(long size)
  • int write(ByteBuffer buffer)
  • int write(ByteBuffer src, long position)

File Lock

文件锁可以锁整个文件也可以锁部分文件,分为读锁(共享锁)和写锁(排他锁).
文件锁是文件级别的,并非线程级别或者channel级别的,也就是说两个不同的JVM,一个JVM的进程有一个文件通道占用了文件A的文件写锁,另一个JVM的进程无法就获取该文件A的写锁。

FileChannel获取文件锁
java.nio.channels.FileLock表示文件区域上的锁的令牌。

  • FileLock lock()
  • FileLock lock(long position, long size, boolean shared)
  • FileLock tryLock()
  • FileLock tryLock(long position, long size, boolean shared)

Mapping Files into Memory

FileChannel声明了map()方法可以将一个打开文件的文件区域映射到java.nio.MappedByteBuffer实例

在通道之间传输字节

  • long transferFrom(ReadableByteChannel src, long position, long count)
  • long transferTo(long position, long count, WritableByteChannel target)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容