什么是Channel
通道是一个对象,它表示到硬件设备、文件、网络套接字、应用程序组件或另一个能够执行写、读和其他I/O操作的实体的开放连接。
通道有效地在字节缓冲区和基于操作系统的I/O服务源或目的地之间传输数据
通道是访问I/O服务的网关。
通道使用字节缓冲区作为发送和接收数据的endpoint
操作系统文件句柄或文件描述符与通道之间通常存在一对一的对应关系,当你在文件上下文中使用通道时,通道通常会连接到打开的文件描述符。尽管通道比文件描述符更抽象,但它们仍然能够对操作系统的I/O设施建模
java中提供了java.nio.channels
和java.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()
方法,该方法描述了到文件的打开连接。事实证明,FileInputStream
和FileOutputStream
也提供了相同的方法。相反java.io.FileReader
和java.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)