Java NIO

文章目录

  • Java NIO
    • 简介
      • 什么是Java NIO
      • NIO与IO的区别
    • 通道与缓冲区
      • 缓冲区(Buffer)
        • 缓冲区的结构
        • 缓冲区的字段(属性)
        • 缓冲区的方法
        • 深入了解缓冲区的属性与方法
      • 通道(Channel)
        • 通道的结构
        • 通道的方法
        • 深入了解非直接与直接缓冲区的区别
          • 非直接与直接缓冲区+通道运输
          • transferFrom,transferTo 通道运输
          • 分散(Scatter)与聚集(Gather)
      • 字符集
    • 阻塞与非阻塞
      • 阻塞
      • 非阻塞
        • 选择器(Selector)
          • SocketChannel
          • DetagramChannel
          • Pipe

简介

什么是Java NIO

NIO :non-blocking IO (非阻塞IO)

Java NIO 是 Java1.4 引入的一个新的IO API

NIO与IO有同样的作用和目的,都是为了读写文件

NIO与IO的区别

NIO IO
非阻塞IO 阻塞IO
面向缓冲区(双向) 面向流(单向)
选择器

总结:NIO支持面向缓冲区(Buffer),通道(channel)的IO操作,使用选择器来实现非阻塞IO,以更高效的方式进行文件的读写

下文将对NIO的三个重要的新名词:缓冲区(Buffer),通道(Channel),选择器(selector) 进行描述

通道与缓冲区

通道是打开IO设备的连接,缓冲区是用于容纳数据的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通道用于传输,缓冲区用于存取

每个通道可以连接一个IO设备,所以读写文件一般使用两个通道

可以想象缓冲区运行在通道上,所以一般本地读写文件使用一个缓冲区即可

缓冲区(Buffer)

缓冲区的结构

java.nio包中有Buffer抽象类,它有着除boolean类型外的代表七个基本类型的子类

它们基本类似,只是各自管理的数据类型不同(后面使用ByteBuffer来举例)

实际上这些缓冲区使用数组来实现的

比如:ByteBuffer就是用byte[]来实现

缓冲区又分为直接缓冲区和非直接缓冲区

  • 非直接缓冲区

  • 直接缓冲区

非直接缓冲区:读取物理磁盘中的某个文件时,会把读到的数据放在内核地址中,然后经过copy到JVM的用户地址空间(写的过程类似)都需要一个copy的步骤

直接缓冲区:将缓冲区建立在物理内存中,通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,避免Java堆和物理内存来回复制数据

缓冲区的字段(属性)

Buffer中有四个重要的属性

// Invariants: mark <= position <= limit <= capacity
//标记索引,通过reset()回到此位置
private int mark = -1;
//操作下一个数据的索引
private int position = 0;
//分界线索引
private int limit;
//缓冲区总容量,因为底层是数组,所以不可变
private int capacity;

缓冲区有读,写两种模式,默认写,使用flip()方法切换成读模式

切换成读模式的时候,之前写到的最后位置就是limit分界线的位置

属性之间需要满足的关系:mark <= position <= limit <= capacity

缓冲区的方法

Buffer的子类提供了操作数据的两个重要方法:

  1. put():在缓冲区上写数据
  2. get():在缓冲区上读数据

其中有很多重载方法,方便多种实现

可以通过静态方法allocate()来创建非直接缓冲区

public static ByteBuffer allocate(int capacity)

通过静态方法allocateDirect()来创建直接缓冲区只有ByteBuffer有直接缓冲区(只有ByteBuffer有这个方法)

这个DirectByteBuffer对象就是在堆中操作那块物理内存的引用

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

还可以通过FileChannel.map()通道方法(后文会介绍)来创建DirectByteBuffer的父类一个MappingByteBuffer映射字节缓冲区(也是直接缓冲区)

public abstract MappedByteBuffer map(MapMode mode,long position, long size)throws IOException;
//模式,当前位置,总量
//模式:/**只读* Mode for a read-only mapping.*/public static final MapMode READ_ONLY= new MapMode("READ_ONLY");/**读写* Mode for a read/write mapping.*/public static final MapMode READ_WRITE= new MapMode("READ_WRITE");/**写时复制* Mode for a private (copy-on-write) mapping.*/public static final MapMode PRIVATE= new MapMode("PRIVATE");

使用方法isDirect()来判断此缓冲区是否是一个直接缓冲区

//创建非直接缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//创建直接缓冲区 直接缓冲区只能是Byte
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);System.out.println("buffer.isDirect():buffer是直接缓冲区吗? "+buffer.isDirect());//false
System.out.println("directBuffer.isDirect():buffer是直接缓冲区吗? "+directBuffer.isDirect());//true
深入了解缓冲区的属性与方法

接下来看一段代码,详细的了解缓冲区的属性与方法

import org.junit.Test;import java.nio.Buffer;
import java.nio.ByteBuffer;/*** @author Tc.l* @Date 2020/10/24* @Description: 缓冲区负责数据的存取(boolean类型无, 其他七大数据类型都有)*/
public class _1缓冲区的数据读取 {@Testpublic void test() {//allocate(): 新建一个容量为1024的缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);lookFiled(buffer,"allocate(): 新建一个容量为1024的缓冲区");//put(): 将数据存入缓冲区String s = "abcdef";buffer.put(s.getBytes());lookFiled(buffer,"put(): 将数据abcdef存入缓冲区");//flip() 切换读模式buffer.flip();System.out.println("切换为读模式");//get(): 读缓冲区中的数据byte[] bytes = new byte[buffer.limit()];buffer.get(bytes,0,2);lookFiled(buffer,"get(): 读2个缓冲区中的数据");//读到的数据System.out.print("读到的数据:");for (byte b : bytes) {if (b==0){break;}System.out.print((char)b);}System.out.println();//mark(): 标记位置buffer.mark();System.out.println("mark标记当前位置");//get(): 读缓冲区中的数据buffer.get(bytes,2,2);lookFiled(buffer,"get(): 再读2个缓冲区中的数据");//读到的数据System.out.print("读到的数据:");for (byte b : bytes) {if (b==0){break;}System.out.print((char)b);}System.out.println();//reset(): 恢复到mark标记的位置buffer.reset();lookFiled(buffer,"reset(): 恢复到mark标记的位置");//rewind():回到0位置buffer.rewind();lookFiled(buffer,"rewind():回到0位置");}private void lookFiled(Buffer buffer,String s) {System.out.println();System.out.println("================"+s+"======================");System.out.println("position当前位置:" + buffer.position());System.out.println("limit分界线位置:" + buffer.limit());System.out.println("capacity总容量位置:" + buffer.capacity());if (buffer.hasRemaining()){System.out.println("remaining缓冲区中还可以操作的数量:"+buffer.remaining());}System.out.println();}}

输出内容:

/*
================allocate(): 新建一个容量为1024的缓冲区======================
position当前位置:0
limit分界线位置:1024
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:1024================put(): 将数据abcdef存入缓冲区======================
position当前位置:6
limit分界线位置:1024
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:1018切换为读模式================get(): 读2个缓冲区中的数据======================
position当前位置:2
limit分界线位置:6
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:4读到的数据:ab
mark标记当前位置================get(): 再读2个缓冲区中的数据======================
position当前位置:4
limit分界线位置:6
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:2读到的数据:abcd================reset(): 恢复到mark标记的位置======================
position当前位置:2
limit分界线位置:6
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:4================rewind():回到0位置======================
position当前位置:0
limit分界线位置:6
capacity总容量位置:1024
remaining缓冲区中还可以操作的数量:6
*/

通道(Channel)

通道的结构

通道用于连接IO设备,本身不能直接访问数据,只能与Buffer交互

通道基本都在java.nio.channels包下

  • Channel接口四个重要的实现类

    1. FileChannel 读写操作文件的通道
    2. DatagramChannel 通过UDP读写网络中的数据通道
    3. SocketChannel 通过TCP读写网络中的数据
    4. ServerSocketChannel 服务端监听新来的TCP连接,为每个新连接创建一个SocketChannel
通道的方法
  • 获得通道的三个方式

    1. 对支持通道的类,提供了getChannel()方法
    2. JDK 7 各个通道提供了open(Path path, OpenOption… options)静态方法
    3. JDK 7 工具类Files.newByteChannel(Path path, OpenOption… options)
  • 通道的读写方法

    • Channel.read(Buffer) : 读通道的数据写到缓冲区中

      • 对于通道来说: 这是在读
      • 对于缓冲区来说: 这是在写
    • Channel.write(Buffer) :读缓冲区的数据写到通道中

      • 对于通道来说: 这是在写
      • 对缓冲区来说: 这是在读

      因为对缓冲区来说在读,所以调用write()方法前,记得使用flip()让缓冲区切换为读模式

    通道的read(),write()方法都是以通道作为本身来设计的

深入了解非直接与直接缓冲区的区别

接下来用一段代码进行本地文件的读写操作,并展示缓冲区,通道的使用

非直接与直接缓冲区+通道运输
 /*** 通道传输 + 非直接缓冲区* 44秒*/@Testpublic  void test1() {LocalDateTime start = LocalDateTime.now();try (//原文件FileInputStream fis = new FileInputStream("D:\\QLDownload\\熊出没·奇幻空间\\熊出没·奇幻空间 熊出没奇幻空间 蓝光(1080P).qlv");//目标文件FileOutputStream fos = new FileOutputStream("D:\\QLDownload\\熊出没·奇幻空间\\2.qlv");//获得连接目标文件的通道FileChannel outChannel = fos.getChannel();//获得连接源文件的通道FileChannel inChannel = fis.getChannel();) {//创建非直接缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);//管道.read(缓冲区): 读管道中的数据写到缓冲区中while ((inChannel.read(buffer) != -1)) {//缓冲区切换为读模式buffer.flip();//管道.write(缓冲区): 读缓冲区的数据写到管道中outChannel.write(buffer);//清空缓冲区buffer.clear();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}LocalDateTime end = LocalDateTime.now();Duration between = Duration.between(start, end);System.out.println(between.getSeconds() + "秒");}/*** 通道传输 + 直接缓冲区* 我的情况:* 一运行程序,文件夹中的视频就已经复制好了* 但是程序需要JVM去垃圾收集掉直接缓冲区,程序才会结束 所以需要很久** 107秒 无System.gc();* 9秒   有System.gc();* System.gc() 不一定会去垃圾回收*/@Testpublic  void test2() {LocalDateTime start = LocalDateTime.now();try (FileChannel inChannel = FileChannel.open(//Path path:地址 OpenOption... options:指定打开文件的方式Paths.get("D:\\QLDownload\\熊出没·奇幻空间\\熊出没·奇幻空间 熊出没奇幻空间 蓝光(1080P).qlv"),//只读StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("D:\\QLDownload\\熊出没·奇幻空间\\2.qlv"),//CREATE_NEW:如果文件存在就报错,不存在就创建(安全)//CREATE:存在就覆盖StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW)) {//通过map(模式,当前位置,总量)方法创建 映射字节缓冲区//MappedByteBuffer是DirectByteBuffer的父类 都可以通过这个引用操作那块物理内存MappedByteBuffer inMappedBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMappedBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());byte[] bytes = new byte[inMappedBuffer.limit()];//读源文件管道的直接缓冲区的数据写到bytes中inMappedBuffer.get(bytes);//读bytes中的数据写到目标文件管道的直接缓冲区中outMappedBuffer.put(bytes);} catch (IOException e) {e.printStackTrace();}System.gc();LocalDateTime end = LocalDateTime.now();Duration between = Duration.between(start, end);System.out.println(between.getSeconds() + "秒");}
transferFrom,transferTo 通道运输

还有transferFrom()transferTo()方法直接通道传输,底层帮我们写直接缓冲区

/*** 通道传输* 使用transferFrom和transferTo直接通道传输,底层帮我们写直接缓冲区* 12s*/@Testpublic  void test3() {LocalDateTime start = LocalDateTime.now();try (FileChannel inChannel = FileChannel.open(//Path path:地址 OpenOption... options:指定打开文件的方式Paths.get("D:\\QLDownload\\熊出没·奇幻空间\\熊出没·奇幻空间 熊出没奇幻空间 蓝光(1080P).qlv"),//只读StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("D:\\QLDownload\\熊出没·奇幻空间\\2.qlv"),//CREATE_NEW:如果文件存在就报错,不存在就创建(安全)//CREATE:存在就覆盖StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE_NEW)) {//inChannel.transferTo(long position, long count,WritableByteChannel target):// 将inChannel管道中position位置,count长度的字节数据 传送到 目标可写字节管道中//inChannel.transferTo(0, inChannel.size(), outChannel);//outChannel.transferFrom(ReadableByteChannel src,long position, long count)//从可读字节管道中position位置,count长度的字节数据 传送到 此管道中//盲猜 方法内部自己使用了直接缓冲区outChannel.transferFrom(inChannel,0,inChannel.size());} catch (IOException e) {e.printStackTrace();}LocalDateTime end = LocalDateTime.now();System.out.println(Duration.between(start, end).getSeconds() + "s");}
分散(Scatter)与聚集(Gather)

分散读取: 一个通道中的数据分散读取到成多个缓冲区中(按照顺序)

聚集写入: 把多个缓冲区的数据聚集写入到一个通道中 (按照顺序)

实际就是用通道的read(),wirte()的重载方法,放进去一个缓冲区数组

public final long read(ByteBuffer[] dsts) throws IOException {return read(dsts, 0, dsts.length);
}public final long write(ByteBuffer[] srcs) throws IOException {return write(srcs, 0, srcs.length);
}

如果是图片的话,不太建议这种方式,只有当第一个缓冲区比图片大时才能运输成功图片,否则打开的不是原来的图片了

 @Testpublic  void test() {try (//RandomAccessFile(String name文件位置, String mode模式:读,读写等)RandomAccessFile accessInFile = new RandomAccessFile("D:\\Tc.l\\学习\\依赖.txt", "rw");FileChannel channel1 = accessInFile.getChannel();RandomAccessFile accessOutFile = new RandomAccessFile("D:\\Tc.l\\学习\\1.txt", "rw");FileChannel channel2 = accessOutFile.getChannel();) {ByteBuffer buffer1 = ByteBuffer.allocate(100);ByteBuffer buffer2 = ByteBuffer.allocate(1024);ByteBuffer[] buffers = {buffer1,buffer2};//把通道1中的数据 写到多个缓冲区中(按顺序,写满了就换下一个)channel1.read(buffers);//打印多个缓冲区中的数据for (ByteBuffer buffer : buffers) {System.out.println(new String(buffer.array(),0,buffer.limit()));}//把每一个缓冲区都切换成读模式for (ByteBuffer buffer : buffers) {buffer.flip();}//把多个缓冲区中的数据 写到 通道2中channel2.write(buffers);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}

字符集

在java.nio.charset.Charset包中

可以拿到编码器,解码器,可以对缓冲区进行编码与解码

 @Testpublic void printAvailableCharsets() {//打印所有可用字符集SortedMap<String, Charset> map = Charset.availableCharsets();Set<Map.Entry<String, Charset>> entrySet = map.entrySet();for (Map.Entry<String, Charset> entry : entrySet) {System.out.println(entry.getKey() + "===" + entry.getValue());}}@Testpublic void test() throws CharacterCodingException {Charset charset = Charset.forName("UTF-8");//得到UTF-8编码器CharsetEncoder charsetEncoder = charset.newEncoder();//得到UTF-8解码器CharsetDecoder charsetDecoder = charset.newDecoder();CharBuffer buffer = CharBuffer.allocate(1024);buffer.put("采用UTF-8编码");//切换读模式buffer.flip();//使用UTF-8编码器进行编码 得到字节缓冲区ByteBuffer encodeBuffer = charsetEncoder.encode(buffer);for (int i = 0; i < 17; i++) {System.out.println(encodeBuffer.get());}//使用UTF-8解码器对刚刚编的码 进行 解码(事先需要把刚刚编的码切换模式)encodeBuffer.flip();CharBuffer decodeBuffer = charsetDecoder.decode(encodeBuffer);System.out.println(decodeBuffer.toString());System.out.println("==================================================");//使用GBK解码器对刚刚编的码 进行 解码(发生乱码)Charset gbk = Charset.forName("GBK");CharsetDecoder gbkDecoder = gbk.newDecoder();encodeBuffer.flip();CharBuffer gbkDecodeBuffer = gbkDecoder.decode(encodeBuffer);System.out.println(gbkDecodeBuffer.toString());/*采用UTF-8编码==================================================閲囩敤UTF-8缂栫爜*/}

阻塞与非阻塞

阻塞

传统IO是阻塞式的,某线程调用读写操作时,会被阻塞直到有数据被读取或写入,在被阻塞期间线程不能执行其他任务

因此在网络通信时,服务端要为每一个客户端提供一个线程来处理读写,当客户端数量多时,性能会下降

模拟阻塞式网络通信

@Testpublic void client() {try (//1.获得通道SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));FileChannel inFileChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)) {//2.指定缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//3.读取本地文件,发送到服务器while ((inFileChannel.read(buffer)) != -1) {buffer.flip();sChannel.write(buffer);buffer.clear();}//相当于告诉服务端输入结束了sChannel.shutdownOutput();//接受服务器端的反馈int len = 0;while ((len = sChannel.read(buffer))!=-1){buffer.flip();System.out.println(new String(buffer.array(),0,len));buffer.clear();}} catch (IOException e) {e.printStackTrace();}}@Testpublic void server() {try (//1.获得通道ServerSocketChannel ssChannel = ServerSocketChannel.open();FileChannel outFileChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {//2.绑定连接ssChannel.bind(new InetSocketAddress(9898));//3.指定缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//4.拿到客户端通道SocketChannel sChannel = ssChannel.accept();//5.接受客户端数据,保存到本地while ((sChannel.read(buffer)) != -1) {buffer.flip();outFileChannel.write(buffer);buffer.clear();}//6.给客户端响应buffer.put("服务端接受数据成功".getBytes());buffer.flip();sChannel.write(buffer);//7.关闭客户端通道sChannel.close();} catch (IOException e) {e.printStackTrace();}}

非阻塞

Java NIO是非阻塞的,某线程在某通道调用读写操作时,若没有数据被读取或写入,该线程就会执行其他任务(线程通常把非阻塞IO操作的空间时间用在其他通道上执行IO操作)

因此在网络通信时,NIO可以让服务器用一个或多个线程来处理连接服务器端的所有客户端

选择器(Selector)

前面说过选择器是NIO实现非阻塞式的核心

  1. 把客户端通道注册到选择器中

  2. 选择器监控这些客户端通道的状况

  3. 只有某个通道"准备就绪"了,选择器才把它发送到服务端的一个或多个线程上

SelectorSelectableChannel对象的多路复用器

Selector可以同时监控多个SelectableChannel对象(利用Selector可以让一个线程管理多个通道)

  • SelectableChannel结构

    • 使用Selector.open()方法获得选择器

      Selector selector = Selector.open();
      
    • 使用SelectableChannel.register(Selector sel,int ops)方法来将通道注册到此选择器上

      //获得通道
      ServerSocketChannel ssChannel = ServerSocketChannel.open()
      //切换为非阻塞模式
      ssChannel.configureBlocking(false);
      //register(Selector sel[注册到哪个选择器上], int ops[监听事件类型])
      ssChannel.register(selector, SelectionKey.OP_ACCEPT);
      
  • SelectionKey表示SelectableChannelSelector之间的注册关系

    • 监听事件类型可用SelectionKey的四个常量表示
    1. SelectionKey.OP_READ = 1 << 0
    2. SelectionKey.OP_WRITE = 1 << 2
    3. SelectionKey.OP_CONNECT = 1 << 3连接
    4. SelectionKey.OP_ACCEPT = 1 << 4接收

    如果注册时不止一个监听事件可以用逻辑位或|连接

SocketChannel

SocketChannel是一个连接到TCP网络套接字的通道

模拟非阻塞式网络通信

 @Testpublic void client() {try (//1.得到通道SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9890))) {//2.切换成非阻塞式sChannel.configureBlocking(false);//3.创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);//4.发送文件LocalDateTime now = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");String s = now.format(formatter);buffer.put(s.getBytes());buffer.flip();sChannel.write(buffer);buffer.clear();} catch (IOException e) {e.printStackTrace();}}@Testpublic void server() {try (   //1.获得通道ServerSocketChannel ssChannel = ServerSocketChannel.open()) {//2.切换成非阻塞式ssChannel.configureBlocking(false);//3.绑定连接ssChannel.bind(new InetSocketAddress(9890));//4.获取选择器Selector selector = Selector.open();//5.把通道注册到选择器上,并指定监听事件//register(Selector sel[注册到哪个选择器上], int ops[SelectionKey属性常数])ssChannel.register(selector, SelectionKey.OP_ACCEPT);//6.轮询式获取选择器上已经准备就绪的事件while (selector.select() > 0) {//7.获得当前选择器中所有注册的 选择键(就绪的监听事件)Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {//8.获取准备就绪的事件SelectionKey key = iterator.next();//9.判断具体是什么事件准备就绪if (key.isAcceptable()) {//10.接受就绪事件 获得客户端SocketChannel sChannel = ssChannel.accept();//11.切换非阻塞式sChannel.configureBlocking(false);//12.把该通道注册到选择器上sChannel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {//13. 获得当前选择器上"读就绪"的通道SocketChannel sChannel = (SocketChannel) key.channel();//14.读取数据ByteBuffer buffer = ByteBuffer.allocate(1024);int len = 0;while ((len = sChannel.read(buffer)) != -1) {buffer.flip();System.out.println(new String(buffer.array(), 0, len));buffer.clear();}}//15.取消选择键iterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
DetagramChannel

DatagramChannel是一个能收发UDP包的通道

 @Testpublic void client() {try (DatagramChannel dc = DatagramChannel.open()) {dc.configureBlocking(false);ByteBuffer buffer = ByteBuffer.allocate(1024);LocalDateTime now = LocalDateTime.now();buffer.put(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).getBytes());buffer.flip();dc.send(buffer, new InetSocketAddress("127.0.0.1", 8090));} catch (IOException e) {e.printStackTrace();}}@Testpublic void server() {try (DatagramChannel dc = DatagramChannel.open()) {dc.configureBlocking(false);Selector selector = Selector.open();dc.bind(new InetSocketAddress(8090));dc.register(selector, SelectionKey.OP_READ);while(selector.select()>0){Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();if (key.isReadable()) {ByteBuffer buffer = ByteBuffer.allocate(1024);dc.receive(buffer);buffer.flip();System.out.println(new String(buffer.array(),0,buffer.limit()));buffer.clear();}}iterator.remove();}} catch (IOException e) {e.printStackTrace();}}
Pipe

Pipe是管道,用于2个线程间单向数据连接

从Pipe的sink通道写入,从source通道读取

 @Testpublic void test1(){try {Pipe pipe = Pipe.open();ByteBuffer buffer = ByteBuffer.allocate(1024);//发送数据Pipe.SinkChannel sinkChannel = pipe.sink();buffer.put(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss")).getBytes());buffer.flip();sinkChannel.write(buffer);//接受数据Pipe.SourceChannel sourceChannel = pipe.source();ByteBuffer allocate = ByteBuffer.allocate(1024);int len = sourceChannel.read(allocate);allocate.flip();System.out.println(new String(allocate.array(),0,len));//关闭sinkChannel.close();sourceChannel.close();} catch (IOException e) {e.printStackTrace();}}

学习视频:尚硅谷NIO
如有错误,麻烦指出,感谢~

一篇通俗易懂的文章初探NIO相关推荐

  1. xnio java_java基础篇---新I/O技术(NIO)

    在JDK1.4以前,I/O输入输出处理,我们把它称为旧I/O处理,在JDK1.4开始,java提供了一系列改进的输入/输出新特性,这些功能被称为新I/O(NEW I/O),新添了许多用于处理输入/输出 ...

  2. 转载一篇不错的文章:谈谈“野生”Java程序员学习的道路

    逛论坛看到的一篇不错的文章,特此转载:本文转载自:左潇龙  原文链接:点击打开链接 引言 很尴尬的是,这个类型的文章其实之前笔者就写过,原文章里,笔者自称LZ(也就是楼主,有人说是老子的简写,笔者只想 ...

  3. 海纳百川而来的一篇相当全面的Java NIO教程

    目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...

  4. 【操作系统】IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析

    [操作系统]IO模型篇之从BIO.NIO.AIO到内核select.epoll剖析! 参考资料: Java网络编程-IO模型篇 [Redis]网络模型:Redis的IO多路复用 [操作系统]全面解析I ...

  5. 关于元数据,全网最通俗易懂的文章!

    " 荐:这篇文章出至我的前同事--90后美女程序员(龚菲).关于元数据.元数据管理的文章有很多,但要说是最通俗易懂的还得是这一篇. 本文原文在这里:<关于元数据,全网最通俗易懂的文章! ...

  6. NBT-19年2月刊4篇35分文章聚焦宏基因组研究

    新年4篇35分文章聚焦宏基因组研究 Nature Biotechnology (NBT,自然生物技术,IF 35.7)在2019年2月刊(https://www.nature.com/nbt/volu ...

  7. NBT-新年4篇35分文章聚焦宏基因组研究

    文章目录 新年4篇35分文章聚焦宏基因组研究 1. 超高速细菌基因组检索技术 摘要 序列搜索方法 2. 宏基因组中设计全面可扩展探针捕获序列多样性 摘要 CATCH设计探针 3. 1520个人类肠道可 ...

  8. 深度学习概述:从感知机到深度网络(找到一篇大牛的文章快围观)

    https://www.toutiao.com/i6652554938519912968/ 2019-01-31 15:10:48 前言 别说我不宠粉,分享一篇大佬的文章,没办法书读得少不知道怎么去形 ...

  9. 学习笔记:The Log(我所读过的最好的一篇分布式技术文章

     学习笔记:The Log(我所读过的最好的一篇分布式技术文章)         前言 这是一篇学习笔记. 学习的材料来自Jay Kreps的一篇讲Log的博文. 原文很长,但是我坚持看完了,收获 ...

  10. 我历时3年才写了10余篇源码文章,但收获了100w+阅读

    你好,我是若川.最近来了一些读者朋友,在这里简单介绍自己的经历,也许对你有些启发.之前发过这篇文章,现在修改下声明原创,方便保护版权. 最近组织了源码共读活动1个月,200+人,一起读了4周源码,很多 ...

最新文章

  1. 破纪录了!用 Python 实现自动扫雷!
  2. android系统属性获取及设置
  3. 【AI】人工智能深度学习入门路线
  4. 原生Java代码拷贝目录
  5. JS小功能(操作Table--动态添加删除表格及数据)实现代码
  6. Nginx源码分析(3)
  7. 双向队列(STL做法)
  8. 3.Excel数据透视表
  9. C++11新特性之八——函数对象function
  10. WinSock I/O 模型 -- IOCP 模型
  11. mongodb数据库的安装 for windows版本 0916
  12. 高通宣布与华为达成新专利授权协议,华为砸下18亿美元
  13. awk处理带有空格的字符串_五分钟入门文本处理三剑客grep awk sed
  14. 设计一台模型计算机 实现下列指令系统,基本模型机的设计与实现1
  15. python基础之Day20part1
  16. elasticsearch批量数据导入和导出
  17. java时间轮定时器_基于时间轮的定时器
  18. linux文件增加自定义属性,Linux 笔记...文件和目录属性useradd、userdel、usermod 、passwd...
  19. 常用数列总结性质记录
  20. 世界著名的十大定律,你都知道吗

热门文章

  1. 高薪利器:四大热门云计算认证推荐
  2. linux进程阻塞例子,linux阻塞与非阻塞驱动例子
  3. 如何实现Android端的录屏采集
  4. PS 批处理切图去空白
  5. png怎么转ico图标?一招教你如何图片转格式
  6. CCL“中国法研杯”相似案例匹配评测竞赛 - TOP队伍攻略分享
  7. wps 安装字体手册
  8. matlab 数字显示完整,如何在matlab中同时显示字符串和数字?
  9. 艾默生黑色无线电池701PBKKF
  10. 解决win10自动锁屏问题的一个方法