NIO教程(2)

简介: NIO教程

NIO教程(1)https://developer.aliyun.com/article/1530795

4.5 NIO核心二:通道(Channel)通道Channe概述

通道(Channel):由java.nio.channels包定义的, Channel表示I0源与目标打开的连接。Channel类似于传统的“流"。只不过Channel本身不能直接访问数据,channel只能与Buffer进行交互。

1、NIO的通道类似于流,但有些区别如下:

  • 通道可以同时进行读写,而流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲读数据,也可以写数据到缓冲:
    2、Bl0中的stream是单向的,例如FilelnputStream对象只能进行读取数据的操作,而NIO中的通道(Channel)是双向的,可以读操作,也可以写操作。
    3、Channel在NIO中是一个接口
    public interface channe1 extends closeable{}
    常用的Channel实现类
  • FileChannel:用于读取、写入、映射和操作文件的通道。
  • DatagramChannel:通过UDP读写网络中的数据通道。
  • SocketChannel:通过TCP读写网络中的数据。
  • ServerSocketChannel:可以监听新进来的TCP 连接,对每一个新进来的连接都会创建一个SocketChannel。【ServerSocketChanne类似ServerSocket , SocketChannel类似Socket】

FileChannel 类

获取通道的一种方式是对支持通道的对象调用getChannel() 方法。支持通道的类如下:

  • FileInputStream
  • FileOutputStream
  • RandomAccessFile
  • DatagramSocket
  • Socket
  • ServerSocket 获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。或者通过通道的静态方法 open() 打开并返回指定通道

FileChannel的常用方法

int read(ByteBuffer dst) 从 从  Channel 到 中读取数据到  ByteBuffer

long  read(ByteBuffer[] dsts) 将 将  Channel 到 中的数据“分散”到  ByteBuffer[]

int  write(ByteBuffer src) 将 将  ByteBuffer 到 中的数据写入到  Channel

long write(ByteBuffer[] srcs) 将 将  ByteBuffer[] 到 中的数据“聚集”到  Channel

long position() 返回此通道的文件位置

FileChannel position(long p) 设置此通道的文件位置

long size() 返回此通道的文件的当前大小

FileChannel truncate(long s) 将此通道的文件截取为给定大小

void force(boolean metaData) 强制将所有对此通道的文件更新写入到存储设备中

案例1-本地文件写数据

需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道), 将 “hello,黑马Java程序员!” 写入到 data.txt 中.

package com.itheima;  
  
  
import org.junit.Test;  
  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.OutputStream;  
import java.nio.ByteBuffer;  
import java.nio.channels.FileChannel;  
  
public class ChannelTest {  
    @Test  
    public void write(){  
        try {  
            // 1、字节输出流通向目标文件  
            FileOutputStream fos = new FileOutputStream("data01.txt");  
            // 2、得到字节输出流对应的通道Channel  
            FileChannel channel = fos.getChannel();  
            // 3、分配缓冲区  
            ByteBuffer buffer = ByteBuffer.allocate(1024);  
            buffer.put("hello,黑马Java程序员!".getBytes());  
            // 4、把缓冲区切换成写出模式  
            buffer.flip();  
            channel.write(buffer);  
            channel.close();  
            System.out.println("写数据到文件中!");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

案例2-本地文件读数据

需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道), 将 data01.txt 中的数据读入到程序,并显示在控制台屏幕

public class ChannelTest {
    @Test
    public void read() throws Exception {
        // 1、定义一个文件字节输入流与源文件接通
        FileInputStream is = new FileInputStream("data01.txt");
        // 2、需要得到文件字节输入流的文件通道
        FileChannel channel = is.getChannel();
        // 3、定义一个缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 4、读取数据到缓冲区
        channel.read(buffer);
        buffer.flip();
        // 5、读取出缓冲区中的数据并输出即可
        String rs = new String(buffer.array(),0,buffer.remaining());
        System.out.println(rs);
    }

案例3-使用Buffer完成文件复制

使用 FileChannel(通道) ,完成文件的拷贝。

@Test
public void copy() throws Exception {
    // 源文件
    File srcFile = new File("C:\\Users\\dlei\\Desktop\\BIO,NIO,AIO\\文件\\壁纸.jpg");
    File destFile = new File("C:\\Users\\dlei\\Desktop\\BIO,NIO,AIO\\文件\\壁纸new.jpg");
    // 得到一个字节字节输入流
    FileInputStream fis = new FileInputStream(srcFile);
    // 得到一个字节输出流
    FileOutputStream fos = new FileOutputStream(destFile);
    // 得到的是文件通道
    FileChannel isChannel = fis.getChannel();
    FileChannel osChannel = fos.getChannel();
    // 分配缓冲区
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    while(true){
        // 必须先清空缓冲然后再写入数据到缓冲区
        buffer.clear();
        // 开始读取一次数据
        int flag = isChannel.read(buffer);
        if(flag == -1){
            break;
        }
        // 已经读取了数据 ,把缓冲区的模式切换成可读模式
        buffer.flip();
        // 把数据写出到
        osChannel.write(buffer);
    }
    isChannel.close();
    osChannel.close();
    System.out.println("复制完成!");
}

案例4-分散 (Scatter) 和聚集 (Gather)

分散读取(Scatter ):是指把Channel通道的数据读入到多个缓冲区中去

聚集写入(Gathering )是指将多个 Buffer 中的数据“聚集”到 Channel。

//分散和聚集
@Test
public void test() throws IOException{
    RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
  //1. 获取通道
  FileChannel channel1 = raf1.getChannel();
  
  //2. 分配指定大小的缓冲区
  ByteBuffer buf1 = ByteBuffer.allocate(100);
  ByteBuffer buf2 = ByteBuffer.allocate(1024);
  
  //3. 分散读取
  ByteBuffer[] bufs = {buf1, buf2};
  channel1.read(bufs);
  
  for (ByteBuffer byteBuffer : bufs) {
    byteBuffer.flip();
  }
  
  System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
  System.out.println("-----------------");
  System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
  
  //4. 聚集写入
  RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
  FileChannel channel2 = raf2.getChannel();
  
  channel2.write(bufs);
}

案例5-transferFrom()

从目标通道中去复制原通道数据

@Test
public void test02() throws Exception {
    // 1、字节输入管道
    FileInputStream is = new FileInputStream("data01.txt");
    FileChannel isChannel = is.getChannel();
    // 2、字节输出流管道
    FileOutputStream fos = new FileOutputStream("data03.txt");
    FileChannel osChannel = fos.getChannel();
    // 3、复制
    osChannel.transferFrom(isChannel,isChannel.position(),isChannel.size());
    isChannel.close();
    osChannel.close();
}

案例6-transferTo()

把原通道数据复制到目标通道

@Test
public void test02() throws Exception {
    // 1、字节输入管道
    FileInputStream is = new FileInputStream("data01.txt");
    FileChannel isChannel = is.getChannel();
    // 2、字节输出流管道
    FileOutputStream fos = new FileOutputStream("data04.txt");
    FileChannel osChannel = fos.getChannel();
    // 3、复制
    isChannel.transferTo(isChannel.position() , isChannel.size() , osChannel);
    isChannel.close();
    osChannel.close();
}

4.6 NIO核心三:选择器(Selector)

选择器(Selector)概述

选择器(Selector) 是 SelectableChannle 对象的多路复用器,Selector 可以同时监控多个 SelectableChannel 的 IO 状况,也就是说,利用 Selector可使一个单独的线程管理多个 Channel。Selector 是非阻塞 IO 的核心

  • Java 的 NIO,用非阻塞的 IO 方式。可以用一个线程,处理多个的客户端连接,就会使用到 Selector(选择器)
  • Selector 能够检测多个注册的通道上是否有事件发生(注意:多个 Channel 以事件的方式可以注册到同一个
    Selector),如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管
    理多个通道,也就是管理多个连接和请求。
  • 只有在 连接/通道 真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都
    创建一个线程,不用去维护多个线程
  • 避免了多线程之间的上下文切换导致的开销

选择 器(Selector)的应用

创建 Selector :通过调用 Selector.open() 方法创建一个 Selector。

Selector selector = Selector.open();

向选择器注册通道:SelectableChannel.register(Selector sel, int ops)

//1. 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
//2. 切换非阻塞模式
ssChannel.configureBlocking(false);
//3. 绑定连接
ssChannel.bind(new InetSocketAddress(9898));
//4. 获取选择器
Selector selector = Selector.open();
//5. 将通道注册到选择器上, 并且指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT);

当调用 register(Selector sel, int ops) 将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定。可以监听的事件类型(用 可使用 SelectionKey 的四个常量 表示):

  • 读 : SelectionKey.OP_READ (1)
  • 写 : SelectionKey.OP_WRITE (4)
  • 连接 : SelectionKey.OP_CONNECT (8)
  • 接收 : SelectionKey.OP_ACCEPT (16)
  • 若注册时不止监听一个事件,则可以使用“位或”操作符连接。
int interestSet = SelectionKey.OP_READ|SelectionKey.OP_WRITE 

4.7 NIO非阻塞式网络通信原理分析

Selector 示意图和特点说明

Selector可以实现: 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞 I/O 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

NIO教程(3)https://developer.aliyun.com/article/1530797

相关文章
|
5月前
|
缓存 Java 索引
|
5月前
|
Java API
|
5月前
|
Java
Java NIO系列教程三
​ 今天主要给大家介绍的是Buffer的基本使用这个也是NIO里面最总要的概率之一,里面的操作也是有一些复杂的同时也是需要大家必须要重点掌握的知识点,同时也介绍了一下Selector的用法下一篇文章我们将为大家介绍Pipe管道以及FileLock文件锁这也是NIO里面最后的一分部内容了。
99 0
|
安全 Java API
Java NIO系列教程四【完】-管道-文件锁-异步写入
​ 到此位置NIO的所有的内容都结束了,对于NIO来说主要是各种概念需要大家去理解然后有很多的用法和api也需要大家去熟悉所以想把NIO学懂学好其实并不容易一定要多写案例去测试巩固,也预祝大家能把NIO的知识看懂理顺!!!
99 0
|
网络协议 Java
Java NIO系列教程一
今天主要给大家介绍的是NIO的基本的概念以及Channel中常用的FileChannel的基本的用法,算是对Channel有一个简单的介绍。下一篇文章我们将详细的为大家介绍其他的常用Channel。
122 0
Java NIO系列教程一
|
网络协议 Java
Java NIO系列教程二
​ 今天主要是为大家详细的介绍了常见的各种Channel以及他们的用法,本文编写了大量的案例还需要大家认真的去实践以后才能真正的掌握住。介绍完Channel那么下一篇文章我们就可以为大家介绍Buffer和Selector的具体使用了。
87 0
|
Java
Java NIO系列教程(8)-SocketChannel的最佳实践(下)
Java NIO系列教程(8)-SocketChannel的最佳实践
184 0
|
网络协议 Java
Java NIO系列教程(8)-SocketChannel的最佳实践(上)
Java NIO系列教程(8)-SocketChannel的最佳实践
455 0
|
监控 Java
Java NIO系列教程(六) Selector
Java NIO系列教程(六) Selector
135 0