JAVA NIO简解

1. 了解NIO

在这里插入图片描述

1.1.什么是NIO?

  1. Java nio是Java的一个新的输入输出(NewInput/Output)API,它提供了一些高效的数据处理方式,如缓冲区(buffers)、字符集(charsets)、通道(channels)和选择器(selectors)。
  2. Java NIO可以实现非阻塞式的多路复用输入输出,提高了程序的性能和可扩展性。Java nio是在Java 1.4版本中引入的,后来在Java7中又增加了NIO2,增加了一些新的特性,如文件系统、异步通道和文件监视等。Java NIO的主要类都在java NIO包和其子包中定义。

1.1.NIO和传统IO的区别

    1. JAVA IO是面向流的,JAVA nio是面向缓冲区的。面向流的意思是数据是一个字节一个字节地传输的,面向缓冲区的意思是数据是以块为单位传输的。
    1. JAVA IO是阻塞式的,JAVA nio是非阻塞式的。阻塞式的意思是当一个线程进行读写操作时,它会一直等待直到操作完成,而非阻塞式的意思是当一个线程进行读写操作时,它可以同时进行其他操作,当读写操作完成时会通知线程。
    1. JAVA IO主要有三种类型:字节流、字符流和对象流,JAVA nio主要有三种类型:缓冲区、通道和选择器。缓冲区是用于存储数据的容器,通道是用于连接IO设备的连接,选择器是用于管理多个通道的工具。
    1. JAVA IO主要使用装饰者模式来实现各种功能,JAVA nio主要使用适配器模式来实现各种功能。装饰者模式是通过包装原有的对象来增加新的功能,适配器模式是通过转换原有的对象来适应新的接口。

2.简单体验

ByteBuffer buf = ByteBuffer.allocateDirect(1024) 是在直接内存中的buff,速度更快,零地址转换

2.1.通道 – 读写数据

1.简单读写

//创建一个文件通道
FileChannel fileChannel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE);

//创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);

//从文件通道读取数据到缓冲区
int bytesRead = fileChannel.read(buffer);

//切换缓冲区到读模式
buffer.flip();

//从缓冲区读取数据并打印
while (buffer.hasRemaining()) {
    System.out.print((char) buffer.get());
}

//清空缓冲区
buffer.clear();

//向缓冲区写入数据
buffer.put("Hello, NIO!".getBytes());

//切换缓冲区到写模式
buffer.flip();

//从缓冲区写入数据到文件通道
int bytesWritten = fileChannel.write(buffer);

//关闭文件通道
fileChannel.close();

2.发散聚集

Nio的发散聚集是指使用一个通道分别读写多个缓冲区或者使用多个通道分别读写一个缓冲区的操作。Nio的发散聚集的代码操作如下

//创建一个随机访问文件
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");

//获取文件通道
FileChannel fileChannel = file.getChannel();

//创建两个缓冲区
ByteBuffer buffer1 = ByteBuffer.allocate(10);
ByteBuffer buffer2 = ByteBuffer.allocate(20);

//将两个缓冲区放入一个数组
ByteBuffer[] buffers = {buffer1, buffer2};

//从文件通道读取数据到两个缓冲区,这是发散操作
fileChannel.read(buffers);

//切换缓冲区到读模式
buffer1.flip();
buffer2.flip();

//从两个缓冲区读取数据并打印
while (buffer1.hasRemaining()) {
    System.out.print((char) buffer1.get());
}
System.out.println();
while (buffer2.hasRemaining()) {
    System.out.print((char) buffer2.get());
}
System.out.println();

//清空缓冲区
buffer1.clear();
buffer2.clear();

//向两个缓冲区写入数据
buffer1.put("Hello".getBytes());
buffer2.put("World".getBytes());

//切换缓冲区到写模式
buffer1.flip();
buffer2.flip();

//将两个缓冲区的数据写入到文件通道,这是聚集操作
fileChannel.write(buffers);

//关闭文件通道和随机访问文件
fileChannel.close();
file.close();

3.FileChannel内存映射

FileChannel内存映射文件是指将文件的一部分或全部映射到直接内存中,这样可以提高文件的访问效率,避免了数据在操作系统内存和JVM内存之间的拷贝123。FileChannel内存映射文件的代码操作如下:

//创建一个随机访问文件
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");

//获取文件通道
FileChannel fileChannel = file.getChannel();

//将文件的一部分映射到直接内存中,返回一个MappedByteBuffer对象
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, file.length());

//读取或修改直接内存中的数据
byte[] data = new byte[mappedByteBuffer.limit()];
mappedByteBuffer.get(data);
System.out.println(new String(data));
mappedByteBuffer.put(0, (byte) 'H');

//关闭文件通道和随机访问文件
fileChannel.close();
file.close();

2.2.管道(Pipe)–线程通信

java NIO
管道是两个线程之间的单向数据连接,有一个source通道和一个sink通道,数据会被写到sink通道,从source通道读取

//创建管道
Pipe pipe = Pipe.open();

//获取sink通道
Pipe.SinkChannel sinkChannel = pipe.sink();

//创建缓冲区并写入数据
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.put("Hello, world!".getBytes());
buffer.flip();

//将缓冲区的数据写入到sink通道
while (buffer.hasRemaining()) {
    sinkChannel.write(buffer);
}

//获取source通道
Pipe.SourceChannel sourceChannel = pipe.source();

//创建缓冲区并从source通道读取数据
buffer.clear();
int bytesRead = sourceChannel.read(buffer);

//打印缓冲区的数据
buffer.flip();
while (buffer.hasRemaining()) {
    System.out.print((char) buffer.get());
}

2.3.网络IO

  1. 服务端代码
//服务端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioServer {
    public static void main(String[] args) throws IOException {
        //创建一个ServerSocketChannel对象
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //绑定一个端口号
        serverSocketChannel.bind(new InetSocketAddress(8888));

        //创建一个Selector对象
        Selector selector = Selector.open();

        //将ServerSocketChannel注册到Selector上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //在一个循环中调用Selector的select方法
        while (true) {
            //获取已经就绪的通道集合
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            //遍历就绪的通道集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //获取当前的SelectionKey
                SelectionKey selectionKey = iterator.next();

                //根据不同的事件类型进行处理
                if (selectionKey.isAcceptable()) {
                    //如果是OP_ACCEPT事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = serverSocketChannel.accept();

                    //设置为非阻塞模式
                    socketChannel.configureBlocking(false);

                    //注册到Selector上,关注OP_READ事件
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    //如果是OP_READ事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储读取到的数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    //从通道中读取数据到缓冲区
                    int len = socketChannel.read(buffer);

                    //判断是否读到了数据
                    if (len > 0) {
                        //切换缓冲区到读模式
                        buffer.flip();

                        //打印读取到的数据
                        System.out.println("Server received: " + new String(buffer.array(), 0, len));
                    }
                } else if (selectionKey.isWritable()) {
                    //如果是OP_WRITE事件
                    //获取对应的SocketChannel对象
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储要写入的数据
                    ByteBuffer buffer = ByteBuffer.wrap("Hello, client".getBytes());

                    //将缓冲区的数据写入到通道中
                    socketChannel.write(buffer);
                }

                //移除当前的SelectionKey,避免重复处理
                iterator.remove();
            }
        }
    }
}
  1. 客户端
//客户端代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioClient {
    public static void main(String[] args) throws IOException {
        //创建一个SocketChannel对象
        SocketChannel socketChannel = SocketChannel.open();

        //设置为非阻塞模式
        socketChannel.configureBlocking(false);

        //连接到服务器的地址和端口号
        socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));

        //创建一个Selector对象
        Selector selector = Selector.open();

        //注册到Selector上,关注OP_CONNECT事件
        socketChannel.register(selector, SelectionKey.OP_CONNECT);

        //在一个循环中调用Selector的select方法
        while (true) {
            //获取已经就绪的通道集合
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            //遍历就绪的通道集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //获取当前的SelectionKey
                SelectionKey selectionKey = iterator.next();

                //根据不同的事件类型进行处理
                if (selectionKey.isConnectable()) {
                    //如果是OP_CONNECT事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //如果连接已经完成
                    if (channel.finishConnect()) {
                        //注册到Selector上,关注OP_WRITE事件
                        channel.register(selector, SelectionKey.OP_WRITE);
                    }
                } else if (selectionKey.isWritable()) {
                    //如果是OP_WRITE事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储要写入的数据
                    ByteBuffer buffer = ByteBuffer.wrap("Hello, server".getBytes());

                    //将缓冲区的数据写入到通道中
                    channel.write(buffer);

                    //注册到Selector上,关注OP_READ事件
                    channel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    //如果是OP_READ事件
                    //获取对应的SocketChannel对象
                    SocketChannel channel = (SocketChannel) selectionKey.channel();

                    //创建一个ByteBuffer对象,用于存储读取到的数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);

                    //从通道中读取数据到缓冲区
                    int len = channel.read(buffer);

                    //判断是否读到了数据
                    if (len > 0) {
                        //切换缓冲区到读模式
                        buffer.flip();

                        //打印读取到的数据
                        System.out.println("Client received: " + new String(buffer.array(), 0, len));
                    }
                }

                //移除当前的SelectionKey,避免重复处理
                iterator.remove();
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring4GWT GWT Spring 使得在 Spring 框架下构造 GWT 应用变得很简单,提供一个易于理解的依赖注入和RPC机制。 Java扫雷游戏 JVMine JVMine用Applets开发的扫雷游戏,可在线玩。 public class JVMine extends java.applet.Applet 简单实现!~ 网页表格组件 GWT Advanced Table GWT Advanced Table 是一个基于 GWT 框架的网页表格组件,可实现分页数据显示、数据排序和过滤等功能! Google Tag Library 该标记库和 Google 有关。使用该标记库,利用 Google 为你的网站提供网站查询,并且可以直接在你的网页里面显示搜查的结果。 github-java-api github-java-api 是 Github 网站 API 的 Java 语言版本。 java缓存工具 SimpleCache SimpleCache 是一个简单易用的java缓存工具,用来简化缓存代码的编写,让你摆脱单调乏味的重复工作!1. 完全透明的缓存支持,对业务代码零侵入 2. 支持使用Redis和Memcached作为后端缓存。3. 支持缓存数据分区规则的定义 4. 使用redis作缓存时,支持list类型的高级数据结构,更适合论坛帖子列表这种类型的数据 5. 支持混合使用redis缓存和memcached缓存。可以将列表数据缓存到redis中,其他kv结构数据继续缓存到memcached 6. 支持redis的主从集群,可以做读写分离。缓存读取自redis的slave节点,写入到redis的master节点。 Java对象的SQL接口 JoSQL JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于【自动提示】的需要(如:Google搜索), 而开发的架构无关的公共控件, 以满足该类需求可以通过快速配置来开发。AutoTips基于搜索引擎Apache Lucene实现。AutoTips提供统一UI。 WAP浏览器 j2wap j2wap 是一个基于Java的WAP浏览器,目前处于BETA测试阶段。它支持WAP 1.2规范,除了WTLS 和WBMP。 Java注册表操作类 jared jared是一个用来操作Windows注册表的 Java 类库,你可以用来对注册表信息进行读写。 GIF动画制作工具 GiftedMotion GiftedMotion是一个很小的,免费而且易于使用图像互换格式动画是能够设计一个有趣的动画了一系列的数字图像。使用简便和直截了当,用户只需要加载的图片和调整帧您想要的,如位置,时间显示和处理方法前帧。 Java的PList类库 Blister Blister是一个用于操作苹果二进制PList文件格式的Java开源类库(可用于发送数据给iOS应用程序)。 重复文件检查工具 FindDup.tar FindDup 是一个简单易用的工具,用来检查计算机上重复的文件。 OpenID的Java客户端 JOpenID JOpenID是一个轻量级的OpenID 2.0 Java客户端,仅50KB+(含源代码),允许任何Web网站通过OpenID支持用户直接登录而无需注册,例如Google Account或Yahoo Account。 JActor的文件持久化组件 JFile JFile 是 JActor 的文件持久化组件,以及一个高吞吐量的可靠事务日志组件。 Google地图JSP标签库 利用Google:maps JSP标签库就能够在你的Web站点上实现GoogleMaps的所有功能而且不需要javascript或AJAX编程。它还能够与JSTL相结合生成数据库驱动的动态Maps。 OAuth 实现框架 Agorava Agorava 是一个实现了 OAuth 1.0a 和 OAuth 2.0 的框架,提供了简单的方式通过社交媒体进行身份认证的功能。 Eclipse的JavaScript插件 JSEditor JSEditor 是 Eclipse 下编辑 JavaScript 源码的插件,提供语法高亮以及一些通用的面向对象方法。 Java数据库连接池 BoneCP BoneCP 是一个高性能的开源java数据库连接池实现库。它的设计初衷就是为了提高数据库连接池的性能,根据某些测试数据发现,BoneCP是最快的连接池。BoneCP很小,只有四十几K

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只会写bug的靓仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值