通道

来源:互联网 发布:d3.js v4 api 编辑:程序博客网 时间:2024/04/19 20:29
通道(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();   // 打开 DatagramChannelByteBuffer 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