通道与缓冲区不同,通道 API 主要由接口指定
。不同的操作系统上通道实现(Channel Implementation)会有根本性的差异,所以通道 API 仅仅描述了可以做什么。因此很自然地,通道实现经常使用操作系统的本地代码。通道接口允许您以一种受控且可移植的方式来访问底层的 I/O服务。

首先,我们来更近距离地看一下基本的 Channel 接口。下面是 Channel 接口的完整源码:
1 2 3 4 5
| package java.nio.channels; public interface Channel{ public boolean isOpen( ); public void close() throws IOException; }
|
从 Channel 接口引申出的其他接口都是面向字节的子接口
,包括 Writable ByteChannel 和ReadableByteChannel。这也正好支持了我们之前所学的:通道只能在字节缓冲区上操作。层次结构表明其他数据类型的通道也可以从 Channel
接口引申而来。这是一种很好的类设计,不过非字节实现是不可能的,因为操作系统都是以字节的形式实现底层 I/O 接口的。
打开通道
通道是访问 I/O 服务的导管,打开一个管道,通常就是创建一个管道。I/O 可以分为广义的两大类别:File I/O 和 Stream I/O。那么相应地也有两种类型的通道:文件(file)通道和套接字(socket)通道。通道可以以多种方式创建。
- Socket: 通道有可以直接创建新 socket 通道的工厂方法,分别是
SocketChannel
、ServerSocketChannel
和DatagramChannel
。
- FileChannel: 对象却只能通过在一个打开的
RandomAccessFile
、FileInputStream
或 FileOutputStream
对象上调用getChannel()方法来获取,不能直接创建。
1 2 3 4 5 6 7 8 9
| SocketChannel sc = SocketChannel.open(); ServerSocketChannel ssc = ServerSocketChannel.open(); DatagramChannel dc = DatagramChannel.open();
FileChannel raf = new RandomAccessFile("file", "r").getChannel(); FileChannel channel = new FileInputStream("abc.txt").getChannel(); FileChannel channe = new FileOutputStream("abc.txt").getChannel();
|
使用通道
打开一个管道之后就是管道的字节读写,有 ReadableByteChannel
, WritableByteChannel
,和InterruptibleChannel
。ByteChannel类继承了ReadableByteChannel和WritableByteChannel的属性。然后还有很多很多的Channel了。例如:SocketChannel,FileChannel,ServerSocketChannel等等。

1 2 3 4 5 6 7 8 9 10 11
| public interface ReadableByteChannel extends Channel { public int read(ByteBuffer dst) throws IOException; }
public interface WritableByteChannel extends Channel { public int write(ByteBuffer src) throws IOException; }
public interface ByteChannel extends ReadableByteChannel, WritableByteChannel { }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| package com.xxo.momo.nio;
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 ChannelTest01 {
public static void main(String[] args) throws IOException {
ReadableByteChannel rbyc = Channels.newChannel(System.in); WritableByteChannel wbyc = Channels.newChannel(System.out);
detailChnnel(rbyc, wbyc);
rbyc.close(); wbyc.close(); }
public static void detailChnnel( ReadableByteChannel rbyc, WritableByteChannel wbyc) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024);
while ( rbyc.read( buffer ) != -1 ){
buffer.flip() ;
wbyc.write( buffer ) ;
buffer.compact() ; }
System.out.println("结束程序后--------"); while ( buffer.hasRemaining() ){ wbyc.write( buffer ) ; } }
public static void detailChnnel2( ReadableByteChannel rbyc, WritableByteChannel wbyc) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024); while (rbyc.read(buffer) != -1) { buffer.flip(); while (buffer.hasRemaining()) { wbyc.write(buffer); } buffer.compact(); } } }
|
- SocketChannel和FileChannel都是实现了以上三个接口的。那么,也就是说,他们都有读写的方法,但是对于FileChannel来说,并非如此,因为有的文件可能是只读的,那么,如果调用write方法的,就会抛出NonWritableChannelException异常。
Scatter/Gather
有时候可能为了提高I/O性能,要组合多个缓冲区,或者从多个缓冲区读数据,就可以使用Scatter(分散)
和Gather(聚合)
- Scatter:从一个Channel读取的信息分散到N个缓冲区中Buufer。
- Gather:将N个Buffer里面内容按照顺序发送到一个Channel。

- ReadableByteChannel、WritableByteChannel接口提供了通道的读写功能
- ScatteringByteChannel、GatheringByteChannel接口提供了分散和聚合
1 2 3 4 5 6 7 8 9 10
| public interface ScatteringByteChannel extends ReadableByteChannel{ public long read (ByteBuffer [] dsts) throws IOException; public long read (ByteBuffer [] dsts, int offset, int length) throws IOException; }
public interface GatheringByteChannel extends WritableByteChannel{ public long write(ByteBuffer[] srcs) throws IOException; public long write(ByteBuffer[] srcs, int offset, int length) throws IOException; }
|
- 下面以FileChannel为例(FileChannel implements GatheringByteChannel, ScatteringByteChannel)
- 实例Scatter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| package com.xxo.momo.nio;
import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset;
public class ScatterDemo {
private static Charset charset = Charset.forName("GBK");
public static void main(String[] args) throws IOException { final String fileName = "D:/test.log"; writeData(fileName, "abcde999efghigk12345678");
scatter(fileName);
scatter2(fileName); }
private static void scatter(final String fileName) throws IOException { RandomAccessFile accessFile = new RandomAccessFile(fileName, "r"); FileChannel channel = accessFile.getChannel(); ByteBuffer head = ByteBuffer.allocate(5); ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {head, body}; System.out.println("scatter====共读到多少字节:" + channel.read(buffers));
head.flip(); System.out.println("head缓冲区中的数据:" + charset.decode(head));
body.flip(); System.out.println("body缓冲区中的数据:" + charset.decode(body));
accessFile.close(); channel.close(); }
private static void scatter2(final String fileName) throws IOException {
RandomAccessFile accessFile = new RandomAccessFile(fileName, "r"); FileChannel channel = accessFile.getChannel();
ByteBuffer head = ByteBuffer.allocate(5); ByteBuffer body1 = ByteBuffer.allocate(3); ByteBuffer body2 = ByteBuffer.allocate(5); ByteBuffer body3 = ByteBuffer.allocate(2);
ByteBuffer[] buffers = new ByteBuffer[]{ head, body1, body2, body3}; System.out.println("scatter2====共读到多少字节:" + channel.read(buffers, 0, 3));
head.flip(); System.out.println("head缓冲区中的数据:" + charset.decode(head));
body1.flip(); System.out.println("body1缓冲区中的数据:" + charset.decode(body1));
body2.flip(); System.out.println("body2缓冲区中的数据:" + charset.decode(body2));
body3.flip(); System.out.println("body3缓冲区中的数据:" + charset.decode(body3));
accessFile.close(); channel.close(); }
private static void writeData(final String fileName, String data) throws IOException { RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw"); accessFile.writeBytes(data); accessFile.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package com.xxo.momo.nio;
import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset;
public class GatherDemo {
public static void main(String[] args) throws IOException { final String fileName = "D:/test2.log"; gather(fileName);
gather2(fileName); }
private static void gather(String fileName) throws IOException { RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw"); FileChannel channel = accessFile.getChannel();
ByteBuffer head = ByteBuffer.allocate(5); head.put("abcde".getBytes());
ByteBuffer body = ByteBuffer.allocate(1024); body.put("999efghigk12345678".getBytes());
ByteBuffer[] allBuffers = { head, body };
head.flip(); body.flip();
System.out.println("gather====共写入多少字节:" + channel.write(allBuffers));
accessFile.close(); channel.close(); }
private static void gather2(String fileName) throws IOException {
RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw"); FileChannel channel = accessFile.getChannel();
ByteBuffer head = ByteBuffer.allocate(5); ByteBuffer body1 = ByteBuffer.allocate(3); ByteBuffer body2 = ByteBuffer.allocate(5); ByteBuffer body3 = ByteBuffer.allocate(20);
head.put("abcde".getBytes()); body1.put("999".getBytes()); body2.put("efghi".getBytes()); body3.put("gk12345678".getBytes());
ByteBuffer[] allBuffers = new ByteBuffer[]{ head, body1, body2, body3};
head.flip(); body1.flip(); body2.flip(); body3.flip();
long n = channel.write(allBuffers, 0, 2);
System.out.println("gather2====共写入多少字节:" + n);
accessFile.close(); channel.close(); } }
|
上一篇:Java--NIO之通道(文件通道)
下一篇:Java--NIO之缓冲区进阶