通道

通道(Channel)
       Java NIO 的通道类似流,但又有些不同:
  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步的读写。
  • 通道中的数据总是要先读到一个 Buffer ,或者总是要从一个 Buffer 中写入。
       正如上面所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道。如下图所示:

       Channel 本身是一个接口,此接口定义了如下方法:
void close() throws IOException    // 关闭此通道
boolean isOpen()                   // 判断此通道是否处于打开状态

       Channel 的实现

       这些是Java NIO 中最重要的通道实现:
  • FileChannel : 从文件中读写数据。
  • DatagramChannel : 能通过 UDP 读写网络中的数据。
  • SocketChannel : 能通过 TCP 读写网络中的数据。
  • ServerSocketChannel : 可以监听新进来的 TCP 连接,像 Web 服务器那样。对每一个新进来的连接都会创建一个 SocketChannel。

FileChannel

       FileChannel 是 Channel 的子类,可以进行文件的读/写操作,此类的常用方法如下:
public abstract int read(ByteBuffer dst) throws IOException    // 将内容读入到缓冲区
public abstract int write(ByteBuffer src) throws IOException   // 将内容从缓冲区写入到通道
public abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size) throws IOException  // 将通道的文件区域映射到内存中,同时指定映射模式、文件中的映射文件以及要映射的区域大小
       如果要使用 FileChannel, 则可以依靠 FileInputStream 或 FileOutputStream 类中的 getChannel() 方法取得输入输出的通道。使用通道写入文本的操作:
String[] info = {"MBYD", "WBJBAQBLCA", "www.pegasus.com", "pegasus"}; // 要输出的数据
File file = new File("E://out.txt");
FileOutputStream fileOutputStream = null;    // 文件输出流
fileOutputStream = new FileOutputStream(file);
FileChannel fileChannel = null;      // 声明输出的通道对象
fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);   // 开辟缓冲
for (int i = 0; i < info.length; i++) {
	byteBuffer.put(info[i].getBytes());         // 向缓冲中写入数据
}
byteBuffer.flip();  // 重设缓冲,准备输出
fileChannel.write(byteBuffer);  // 輸出
fileChannel.close();
fileOutputStream.close();
       上面程序使用输出通道将内容全部放到缓冲中,一次性写入到文件中的,实际上 FileChannel 是双向操作,同时可以完成输出和输入数据的功能,下面演示进行读写文件的操作。
File file1 = new File("E://out.txt");
File file2 = new File("E://outnote.txt");
FileInputStream input = null;  // 文件输入流
FileOutputStream output = null; // 文件输出流
input = new FileInputStream(file1);
output = new FileOutputStream(file2);
FileChannel fin = null;    // 声明输入的通道对象
FileChannel fout = null;    // 声明输出的通道对象
fin = input.getChannel();
fout = output.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int temp = 0;
while ((temp = fin.read(buffer)) != -1) {
	buffer.flip();
	fout.write(buffer);
	buffer.clear();
}
fin.close();
fout.close();
input.close();
output.close();

内存映射

       内存映射可以把文件映射到内存中,这样文件内的数据就可以用内存读/写指令来访问,而不是用 InputStream 或 OutputStream 这样的 I/O 操作类,采样此种方式读取文件的速度是最快的。
       Java中访问文件内容的4种方法:
  • RandomAccessFile  随机读取数据,此种访问速度较慢。
  • FileInputStream  文件输入流,使用此种方式速度较慢。
  • 缓冲读取(如BufferedReader) 使用此种方式访问速度较快。
  • 内存映射(MappedByteBuffer) 使用此种方式读取速度较快。
       想要将文件映射到内存中,可以使用 FileChannel 类提供的 map() 方法。map() 方法在使用时要指定映射模式,在内存映射中提供了 3 种模式,这 3 中模式分别由 FileChannel 类中的 3 个常量表示:
public static final FileChannel.MapMode READ_ONLY              // 只读映射模式。 
public static final FileChannel.MapMode READ_WRITE             // 读取/写入映射模式。
public static final FileChannel.MapMode PRIVATE                // 专用(写入时拷贝)映射模式。
       通过一个读取文件的操作来观察如何使用 MappedByteBuffer 读取硬盘上的文件,以操作上面例子中 out.txt 为例:
File file = new File("E://out.txt");
FileInputStream input = new FileInputStream(file);
FileChannel fin = input.getChannel();
MappedByteBuffer mbb = null; // 聲明文件的內存映射
mbb = fin.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); // 将文件映射到内存中
byte[] data = new byte[(int) file.length()];
int foot = 0;
while (mbb.hasRemaining()) {
	data[foot++] = mbb.get();
}
System.out.println(new String(data));
fin.close();
input.close();

文件锁 FileLock

       在 java 新 IO 中提供了文件锁的功能,这样当一个线程将文件锁定之后,其它线程是无法操作此文件的。要想进行文件锁定操作,则使用 FileLock 类完成,此类的对象需要依靠 FileChannel 进行实例化操作。
       FileChannel 类提供的几个方法取得 FileLock 类的实例化对象:
public final FileLock lock() throws IOException        // 获取对此通道的文件的独占锁定
public abstract FileLock lock(long position, long size, boolean shared)
                       throws IOException   // 获取此通道的文件给定区域上的锁定。
public final FileLock tryLock() throws IOException     // 试图获取对此通道的文件的独占锁定。 
public abstract FileLock tryLock(long position, long size, boolean shared)
                          throws IOException           // 试图获取对此通道的文件给定区域的锁定。 并指定锁定位置、锁定大小,属于共享锁定(true)或者独占锁定 (false)
       文件锁的方式有两种:
  • 共享锁 :允许多个线程进行文件的读取操作。
  • 独占锁 :只允许一个线程进行文件的读/写操作。
       文件锁定之后需要依靠 FileLock 类进行解锁,此类的常用方法:
public final boolean isShared()       // 判断此锁定是否为共享的。
public final FileChannel channel()    // 返回文件通道,此锁定保持在该通道的文件上。
public abstract void release() throws IOException         // 释放此锁定。 
public final long size()          // 返回锁定区域的大小,以字节为单位。
       将上面例子中的 out.txt 锁定。
File file = new File("E://out.txt");
FileOutputStream output = new FileOutputStream(file, true);
FileChannel fout = output.getChannel();
FileLock fileLock = fout.tryLock(); // 试图获得此通道的文件锁
if (fileLock != null) {
	System.out.println(file.getName() + "文件锁定 300 秒!");
	Thread.sleep(300000);
	fileLock.release(); // 释放文件锁
	System.out.println(file.getName() + "文件锁定解除了");
}
fout.close();
output.close();
       以上程序在运行时将文件进行独占锁定,这样其它线程在锁定的 300 秒内是无法对此文件进行读写操作的。

DatagramChannel 

Java NIO 中的 DatagramChannel 是一个能收发 UDP 包的通道。因为 UDP 是无连接的网络协议,所以不能像其他通道那样读取和写入。它发送和接收的是数据包。
public class DatagramChannelSender {
	public static void main(String[] args) throws IOException {
		send();
	}
	private static void send() throws IOException {
		DatagramChannel channel = DatagramChannel.open();   // 打开 DatagramChannel
		ByteBuffer buffer = ByteBuffer.wrap("下雨的夜晚很安静".getBytes("utf-8")); // 将 byte 数组包装到缓冲区中,编码方式是 utf-8
		/*
		buffer = ByteBuffer.allocate(60);
		buffer.clear();
		buffer.put("下雨的夜晚很安静".getBytes("utf-8"));
		buffer.flip();
		*/
		channel.send(buffer, new InetSocketAddress("localhost", 10000));
		channel.close();
	}
}
public class DatagramChannelReveiver {
	public static void main(String[] args) {
		try {
			receive();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private static void receive() throws Exception {
		DatagramChannel channel = DatagramChannel.open();
		channel.socket().bind(new InetSocketAddress(10000));
		ByteBuffer buffer = ByteBuffer.allocate(60);
		while (channel.receive(buffer) == null) {
			Thread.sleep(1000);
		}
		buffer.flip();
		String recStr = Charset.forName("utf-8").newDecoder().decode(buffer)
				.toString();
		System.out.println(recStr);
		channel.close();
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值