龙空技术网

javaNIO 学习笔记(四)

非鸽传书 44

前言:

今天小伙伴们对“javanio与io”都比较关切,你们都需要分析一些“javanio与io”的相关文章。那么小编在网上搜集了一些关于“javanio与io””的相关资讯,希望我们能喜欢,大家快快来了解一下吧!

javaNIO 学习笔记(四)Java NIO Selector

selector(选择器)作为 Java NIO的一个组件,它可以检查一个或多个Java NIO通道实例,并确定哪些通道可以读取或写入。通过这种方式,一个线程可以管理多个通道,从而实现多个网络连接。因为是单线程处理多个通道的信息,因此用于处理通道数据的线程数就会减少,这样就可以节省下线程之间切换的时间成本。

开启selector

selector.open()

注册selector

channel.register()

将监听器register到非阻塞channel,并指定监听的事件,并得到一个SelectionKey实例。

通道必须处于非阻塞模式才能与选择器一起使用。这意味着你不能使用FileChannel的选择器,因为FileChannel的不能切换到非阻塞模式。

监听一般有四个事件

这四个事件由四个SelectionKey常量表示:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT:就是就绪

SelectionKey.OP_READ

SelectionKey.OP_WRITE

如果想监听多个事件可以按照如下方式写:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

SelectionKey

一些常见方法

//返回SelectionKey的attachment,attachment可以在注册channel的时候指定。SelectionKey.attachment(object);SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);// 返回该SelectionKey对应的channel。SelectionKey.channel(); // 返回该SelectionKey对应的Selector。SelectionKey.selector(); //返回代表需要Selector监控的IO操作的bit maskSelectionKey.interestOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。SelectionKey.readyOps(); // 创建ready集合的方法int readySet = selectionKey.readyOps();//检查这些操作是否就绪的方法SelectionKey.isAcceptable();//是否可读,是返回 trueboolean isWritable()://是否可写,是返回 trueboolean isConnectable()://是否可连接,是返回 trueboolean isAcceptable()://是否可接收,是返回 true// ---  判断Selector是否对Channel的某种事件感兴趣 ---int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;    Channel channel = key.channel();Selector selector = key.selector();

select()方法:返回值表示有多少通道已经就绪,一旦调用select()方法,并且返回值不为0时,则 可以通过调用Selector的selectedKeys()方法来访问已选择键集合 。

int select():阻塞到至少有一个通道在你注册的事件上就绪了。int select(long timeout):和select()一样,但最长阻塞时间为timeout毫秒。int selectNow():非阻塞,只要有通道就绪就立刻返回。

选择器执行选择的过程,系统底层会依次询问每个通道是否已经就绪,这个过程可能会造成调用线程进入阻塞状态,那么我们有以下三种方式可以唤醒在select()方法中阻塞的线程。

wakeup()方法 :通过调用Selector对象的wakeup()方法让处在阻塞状态的select()方法立刻返回 该方法使得选择器上的第一个还没有返回的选择操作立即返回。如果当前没有进行中的选择操作,那么下一次对select()方法的一次调用将立即返回。close()方法 :通过close()方法关闭Selector该方法使得任何一个在选择操作中阻塞的线程都被唤醒(类似wakeup()),同时使得注册到该Selector的所有Channel被注销,所有的键将被取消,但是Channel本身并不会关闭。

下面的代码是参考文档中对应给出的。

package jniolearn.selector;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;import java.util.Set;/** * @Author: jimmy * @Date: 2020/6/15 22:15 * @Description: */public class SelectorPrc {    public static void main(String[] args) throws IOException {        ServerSocketChannel ssc = ServerSocketChannel.open();        ssc.socket().bind(new InetSocketAddress("localhost", 8080));        // 设置为非则色        ssc.configureBlocking(false);        // 开启一个选择器        Selector selector = Selector.open();        // 注册选择器和监听的事件        ssc.register(selector, SelectionKey.OP_ACCEPT);        while(true) {            int readyNum = selector.select();            if (readyNum == 0) {                continue;            }            Set<SelectionKey> selectedKeys = selector.selectedKeys();            Iterator<SelectionKey> it = selectedKeys.iterator();            while(it.hasNext()) {                SelectionKey key = it.next();                if(key.isAcceptable()) {                    // 接受连接                } else if (key.isReadable()) {                    // 通道可读                } else if (key.isWritable()) {                    // 通道可写                }				// keyIterator.remove()调用。选择器不会从所选键集本身移除SelectionKey实例。处理完通道时,必须这样做。下次通道变为“ready”时,选择器将再次将其添加到选中的键集。                it.remove();            }        }    }}
Java NIO Pipe

pipe的意思是管道,那么前面几篇学习中我说channel也可以理解为管道是错误的,这里更正下,channel应该理解为通道,防止概念混乱。

管道单项连接2给线程的。管道具有源通道和接收通道。将数据写入接收通道。然后可以从源通道读取该数据。

package jniolearn.channel;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.Pipe;/** * @Author: jimmy * @Date: 2020/6/15 23:44 * @Description: */public class NioPipe {    public static void main(String[] args) throws IOException {        // 开启pipe        Pipe pipe = Pipe.open();        // 接受通道开启        Pipe.SinkChannel sinkChannel = pipe.sink();        // 准备数据        String newData = "jimmy pipe" ;        ByteBuffer buf = ByteBuffer.allocate(48);        buf.clear();        buf.put(newData.getBytes());        buf.flip();        // 写入数据        while(buf.hasRemaining()) {            sinkChannel.write(buf);        }        // 接受数据        ByteBuffer buffer = ByteBuffer.allocate(48);        Pipe.SourceChannel sourceChannel = pipe.source();        int len = sourceChannel.read(buffer);        buffer.flip();        System.out.println(new String(buffer.array(), 0, len));        // 4. 关闭管道        sinkChannel.close();        sourceChannel.close();    }}
Java NIO Path

JDK7新增Path接口,Paths工具类,Files工具类。 这些接口和工具类对NIO中的功能进行了高度封装,大大简化了文件系统的IO编程。

分别位于java.nio.file、java.nio.channels包下

Path

Path接口表示的是一个与平台无关的路径(文件和目录都用Path表示)。

Path类中包含了常用的操作路径的方法,

getRoot()  -Path对象的跟路径toAbsolutePath() -Path对象的绝对路径getNameCount() -Path对象里包含的路径数量

Path path = Paths.get("c:\\data\\myfile.txt");

在Unix系统(Linux, MacOS, FreeBSD等)上,上面的绝对路径看起来像这样:

Path = Paths.get("/home/jakobjenkov/myfile.txt");

Path接口的normalize()方法可以规范化路径

String originalPath =        "d:\\data\\projects\\a-project\\..\\another-project";Path path1 = Paths.get(originalPath);System.out.println("path1 = " + path1);Path path2 = path1.normalize();System.out.println("path2 = " + path2);// 规范化路径不包含a-project\..部分,因为这是多余的。被删除的部分没有添加到最终的绝对路径。path1 = d:\data\projects\a-project\..\another-projectpath2 = d:\data\projects\another-project        p1.toFile();    
Paths工具类

Paths工具类包含了两个返回Path对象的静态方法。

static Path get(URI uri) 直接通过路径返回Path对象static Path get(String first, String…more)通过给出的String字符串组装成一个Path对象

package jniolearn;import java.nio.file.Path;import java.nio.file.Paths;/** * @Author: jimmy * @Date: 2020/6/16 00:10 * @Description: */public class PathPrc {    public static void main(String[] args) {        //在传统java.io中, 文件和目录都被抽象成File对象, 即 File file = new File(".");        //NIO.2中则引入接口Path代表与平台无关的路径,文件和目录都用Path对象表示        //通过路径工具类Paths返回一个路径对象Path        Path path = Paths.get(".");        System.out.println("path里包含的路径数量:" + path.getNameCount());        System.out.println("path的根路径: "+path.getRoot());        //path的绝对路径        //对比传统java.io, 取绝对路径 file.getAbsoluteFile()        Path absolutePath = path.toAbsolutePath();        System.out.println("path的绝对路径:");        System.out.println(absolutePath);        System.out.println("absolutePath的根路径: "+absolutePath.getRoot());        System.out.println("absolutePath里包含的路径数量:" + absolutePath.getNameCount());        System.out.println(absolutePath.getName(3));        //以多个String来构建path -》 java.lang.IllegalArgumentException,路径不存在会报错        Path path2 = Paths.get("D:\", "myProject" , "ThreadNotes");        System.out.println(path2);    }}

下一篇学习file和异步Channel。

标签: #javanio与io