javaNIO 学习笔记(四)

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 mask
SelectionKey.interestOps(); 
// 返回一个bit mask,代表在相应channel上可以进行的IO操作。
SelectionKey.readyOps(); 

// 创建ready集合的方法
int readySet = selectionKey.readyOps();
//检查这些操作是否就绪的方法
SelectionKey.isAcceptable();//是否可读,是返回 true
boolean isWritable()//是否可写,是返回 true
boolean isConnectable()//是否可连接,是返回 true
boolean 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-project
path2 = 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。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值