Java-NIO学习笔记

NIO

1. Buffer

buffer 即缓冲区数组,用于存储数据

  • Java提供的Buffer的实现类有:ByteBuffer
    CharBUffer
    ShortBuffer

Buffer有四个重要的属性:

markpositionlimitcapacity

- capacity –>最大容量,一旦声明不能改变。
- limit –>缓存区中可以操作数据的大小(limit后的数据不能读写)
- position –>缓冲区中正在操作数据的位置

Buffer可以是非直接缓冲区和直接缓冲区:

1. 非直接缓冲区

建立在JVM内存中,与本地磁盘读写需要通过两个缓冲区,copy耗时。

1. 通过allocate()获得
ByteBuffer buffer = ByteBuffer.allocate(1024);
2. 存入数据 put()

position –>length

buffer.put("asdasd".getBytes());
3. flip() 后读数据

limit –>position
position –>0

buffer.flip();
4. 取出数据 get()

position –>limit

buffer.get(数组);
5. rewind() 使用后重读

position –>0

6. clear() 清空缓冲区(但数据还在)

position –>0
limit –>capacity

7. mark() – reset()
  1. mark标记位置
  2. reset恢复位置

2. 直接缓冲区

建立在物理内存中(映射文件)
- 使用:长时间存在的数据
1. 效率提高
2. 内存消耗大,直到gc释放

1. 通过allocateDirect()获得
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

2. Channel

Channel即连接通道,负责buffer的运输
- FileChannel(file)、SocketChannel、ServerSocketChannel(tcp)、DatagramChannel(upd)

1. 获取通道

1. 由IO流提供getChannel()
  1. FileInputStream\FileOutStream
  2. RandomAccessFile
  3. Socket
  4. ServerSocket
  5. DatagramSocket
2. 针对各个通道提供的静态方法open()
//StandardOpenOption.READ读/WRITE写/CREATE_NEW没有则创建,有则报错/CREATE有则覆盖
FileChannel.open(Paths.get("文件地址"),StandardOpenOption.READ);

SocketChannel.open(new InetAddress(port));

ServerSocketChannel.open(new InetAddress(IP,port));
3. Files工具类的newByteChannel()

2. 传输(非直接)

  • read() & write()
ByteBuffer buffer = ByteBuffer.allocate(1024);

while(inChannel.read(buffer) != -1){
    //buffer进入读模式
    buffer.flip();

    //写入通道
    outChannel.write(buffer);

    //清空准备下一次写入
    buffer.clear();

}
  • String对象的getBytes()方法提供byte[]数组
  • 使用ByteBuffer提供的wrap方法生成一个Buffer
channel.write(ByteBuffer.wrap("ABCDE".getBytes()));

3. 传输(直接)

内存映射文件

<!-- 内存映射文件 -->
//读
MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY模式,0从,inChannel.size()长度);
//写
MappedByteBuffer outMappedBuffer outChannel.map(MapMode.READ_WRITE,0,inChannel.size());

//操作 -->直接通过缓冲区读写
byte[] dst = new byte[inMappedBuffer.limit()];
inMappedBuffer.get(dst);
outMappedBuffer.put(dst);

//关闭通道

4. 通道间传输

  1. transferFrom()
  2. transferTo()
<!-- 直接缓冲区 -->
inChannel.transferTo(position,count,target)
outChannel.transferFrom(src,position,count)

5. 分散读取(Scatter)与聚集写入 (Gather)

  • Scatter 将通道中的数据分散到各个缓冲区
  • Gather 将多个缓冲区的数据聚集到通道中
buffer1
buffer2
ByteBuffer[] bufs = {buffer1,buffer2};
channel.read(bufs);
for->flip()
channel.write(bufs);

6. Charset字符集

  • 编码:字符串->字节数据
  • 解码:字节数据->字符串
//字符集
Map<String,Charset> map = Charset.availbaleCharset();

Set<Entry<String,Charset>> set = map.entryset

for(Entry<String,Charset> entry:set){
    entry.key;
    entry.value;
}
Charset cs = Charset.forName("utf-8");

//编码器
CharsetEncoder ce = cs.newEncoder();

//解码器
CharsetDecoder cd = cs.newDecoder();

//缓冲区
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("abcde");

//编码:char --> byte
charBuffer.flip();
ByteBuffer byteBuffer = ce.encode(charBuffer);

//解码:byte --> char
byteBuffer.flip();
CharBuffer buffer = cd.decode(byteBuffer)

3. 非阻塞

传统IO阻塞式,即便多线程也无法充分利用资源

1. 选择器Selector

  • 把每一个通道都注册到选择器上
  • 选择器监控这些通道的IO状况

2. 通道Channel

SelectableChannel:
- SocketChannel
- ServerSocketChannel
- DatagramChannel
- Pipe.SinkChannel
- Pipe.SourceChannel

3. 客户端Client

  1. 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("IP",port));
  1. 切换到非阻塞模式
sChannel.configureBlocking(false);
  1. 注册读事件
Selector selector = Selector.open();
sChannel.register(selector,SeletionKey.OP_READ);
  1. 轮询选择器,接收来自服务端的消息
while (true) {
    while (mSelector.select() > 0) {

        Iterator<SelectionKey> iterator = mSelector.selectedKeys().iterator();
        while (iterator.hasNext()) {

            SelectionKey key = iterator.next();
            iterator.remove();

            if (key.isReadable()) {
                //处理信息
                handler.read(key);
            }
        }
    }
}
  1. 新开线程进行写操作
new Thread(() -> {

    //开始(写的)阻塞循环
    Scanner scanner = new Scanner(System.in);
    while (true) {
        try {
            sChannel.write(...);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}).start();

4. 服务端Server

  1. 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
  1. 切换非阻塞模式
ssChannel.configureBlocking(false);
  1. 绑定连接
ssChannel.bind(new InetSocketAddress(port));
  1. 获取选择器
Selector selector = Selector.open();
  1. 注册通道到选择器上
ssChannel.register(selector,ops/*监控状态*/);
<!-- SelectionKey -->
SelectionKey{
    OP_READ
    OP_WRITE
    OP_CONNECT
    OP_ACCEPT
}

这里填写的是OP_ACCEPT,是为了监听来自SocketChannel的连接
6. 轮询式获取选择器上已经准备就绪的事件

while(true){

    while(selector.select() > 0){

        //返回所有事件的集合
        Iterator<SelectionKey> it = selector.selectedKey().iterator();//获取迭代器 

       //迭代获取
        while(it.hasNext()){
           SelectionKey key = it.next();
           iterator.remove();

           <!-- 接收 -->
           if(key.isAcceptable()){       
                //获得通道
                SocketChannel sChannel = ((ServerSocketChannel) key.channel()).accept();

                //切换非阻塞模式
                sChannel.configureBlocking(false);

                //注册,监听读事件
                sChannel.register(selector,SelectionKey.OP_READ);

            }

            <!-- 读 -->
            if(key.isReadable()){
                read(key);
            }

        }
    }
}
  1. 处理信息并轮流发到各个通道。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值