Channels

Introducing Channels

_channel_对象代表了一个开放连接到能够执行IO操作的硬件设备,如文件,网络流,应用组件。_channel_使用_buffer_作为一个传输数据载体。

Channel and Its Children

所有_channel_类都是java.nio.channels.Channel接口的实现类。它声明了两个方法:

  • void close():关闭_channel_
  • boolean isOpen():检查_channel_是否开启着

所有_channel_都共用这两个方法,为了支持IO操作,有两个扩展接口:

  • WritableByteChannle
  • ReadableByteChannel

以上两个接口都是单向接口(只读或只写),java提供了java.nio.channels.ByteChannel接口,该接口是同时支持读写操作。

java还提供了支持异步关闭的java.nio.channels.InterruptibleChannel,实现该接口的_channel_具有异步关闭的功能:当一个线程阻塞在io操作上时,其它线程可以关闭该线程,然后该线程会抛出AsynchronousCloseException。该接口也是可中断的,即其它线程触发该线程的interrupt()方法,会抛出ClosedByInterruptException

两种获得_channel_实例的方法:

  • 使用Channels工具类
  • 通过经典的IO类来获得。

一个例子:

public class UsingChannel {
	@Test
	public void i2o() throws Exception {
		ReadableByteChannel rbc = Channels.newChannel(System.in);
		WritableByteChannel wbc = Channels.newChannel(System.out);
		copy(rbc, wbc);
	}

	/**
	 * 最少的IO操作次数
	 * 
	 * @param rbc
	 * @param wbc
	 * @throws IOException
	 */
	void copy(ReadableByteChannel rbc, WritableByteChannel wbc) throws IOException {
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		int read;
		while ((read = rbc.read(buffer)) != -1) {
			buffer.flip();
			wbc.write(buffer);
			buffer.compact();
		}
		buffer.flip();
		while (buffer.hasRemaining()) {
			wbc.write(buffer);
		}
	}

	/**
	 * 消除数据的复制移动
	 * 
	 * @param rbc
	 * @param wbc
	 * @throws IOException
	 */
	void copy2(ReadableByteChannel rbc, WritableByteChannel wbc) throws IOException {
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		while (rbc.read(buffer) != -1) {
			buffer.flip();
			while (buffer.hasRemaining()) {
				wbc.write(buffer);
			}
			buffer.clear();
		}
	}
}

Channels in Depth

Scatter/Gather I/O

提供一个IO操作横跨多个_buffer_的能力。

一个例子:

	public void scga() throws Exception {
		final int M_size = 1024 * 1024;
		FileInputStream fis = new FileInputStream("./pdf");
		FileOutputStream fos = new FileOutputStream("./pdfc");
		ScatteringByteChannel src = (ScatteringByteChannel) Channels.newChannel(fis);
		// 这样工作
		// ByteBuffer[] buffers = new ByteBuffer[] {
		// ByteBuffer.allocate(M_size), ByteBuffer.allocate(M_size),
		// ByteBuffer.allocate(M_size) };

		// 这样不工作
		ByteBuffer[] buffers = new ByteBuffer[3];
		for (ByteBuffer byteBuffer : buffers) {
			byteBuffer = ByteBuffer.allocate(M_size);
		}
		for (ByteBuffer byteBuffer : buffers) {
			if (byteBuffer == null) {
				System.out.println("foreach can't assign value");
				return;
			}
		}

		// 这样工作
		// for (int i = 0; i < buffers.length; i++) {
		// buffers[i] = ByteBuffer.allocate(M_size);
		// }
		src.read(buffers);
		src.close();
		for (ByteBuffer byteBuffer : buffers) {
			byteBuffer.flip();
		}
		GatheringByteChannel dest = (GatheringByteChannel) Channels.newChannel(fos);
		dest.write(buffers);
		dest.close();
	}

这里发现一个我从来都没考虑过的点:foreach的使用,使用foreach时最好是仅取值,而不更新。切记切记!!!

File Channels

抽象类java.nio.channels.FileChannel用于描述一个文件通道。因为该类实现了InterruptibeChannel,该类是可中断的,该类同时实现了ByteChannelGatheringByteChannelScatteringByteChannel接口。

文件通道是线程安全的!

FileChannel提供了如下方法:

Method Description
void force(boolean metadata) 将文件的所有更新都提交到存储设备
long position() 返回文件指针的位置
FileChannel position(long newPosition) 指定文件指针的
int read(ByteBuffer buffer)
int read(ByteBuffer buffer,long position) 指定开始读取的位置
long size()
FileChannel truncate(long size) 截断文件,size是指文件指针的位置,即将文件指针后的文件截断抛弃掉。
int write(ByteBuffer buffer)
int write(ByteBuffer buffer, long position)

Locking Files

将一个文件锁定或部分锁定是一个重要的特性,但是知道Java1.4,java才支持该功能。这个功能使得JVM进程独占该文件或部分文件,知道该jvm进程释放文件锁。

尽管我们可以锁定整个文件,但通常最好是锁住一小部分。例如,数据库系统可能会锁住单独的表的一行,而不是整个表,以使其它部分能供外部操作,从而提高吞吐量。

每一个文件锁开始于文件的特定的字节位置,然后占据一定长度的字节。文件锁协调进程访问不同的文件区域。

有两种类型的文件锁,排他锁和共享锁。排他锁使得单个写进程,写入到文件,它禁止额外的文件锁同时应用到该区域。共享锁允许多个读进程,同时读取同一个文件区域,它不会禁止其它的共享锁应用到该区域,但是不允许排他锁应用到该问题。

文件锁属于进程锁,而不是线程锁,因此,同一个jvm进程实例下的线程时共享该文件锁的,这要注意。

FileChannel声明了四个方法用于获取排他锁或共享锁:

  • FileLock lock():阻塞方法,可能抛出ClosedChannelExceptionNonWritableChannelExceptionOverlappingFileLockExceptionFileLockInterruptionExceptionAsynchronousCloseException异常。
  • FileLock lock(long position, long size, boolean shared):
  • FileLock tryLock():非阻塞方式的,不管成功与否,立即返回。

以上方法它们每一个都返回一个FileLock对象实例,该对象封装了文件某个区域的锁。该对象具有如下方法:

  • FileChannel channel():返回该文件锁对应的文件通道对象
  • void close():会调用ralease()方法释放锁
  • boolean isShared():判断是否是共享锁
  • boolean isValid():是否有效
  • boolean overlaps(long position, long size):判断文件锁锁定的区域与指定的区域重合
  • long position():返回文件锁相对于文件的开始位置
  • void release():释放文件锁
  • long size():返回文件锁锁定的区域大小
  • String toString():文件锁的信息

尽管文件锁与文件通道关联,当实际上文件锁是与文件通道相关的具体文件相关的。如果不当心,就会进入竞争状态,甚至是死锁。因此,要总是记得释放锁。

posted @ 2017-01-31 00:40  JintaoXIAO  阅读(1171)  评论(0编辑  收藏  举报