NIO
来源:互联网 发布:在上海工作的感受 知乎 编辑:程序博客网 时间:2024/06/07 00:38
一、
1. NIO 和 IO 的区别
Java NIO 的核心:通道(channel 负责传输) 和缓存区(buffer负责存储)
a) 缓冲区i.缓存区(buffer) :在Java nio 中负责数据的存取,缓冲区就是数组,用于存储不同数据类型的数据
根据数据类型不同提供了相应类型的缓冲区(Boolean类型除外)
ByteBuffer
CharBuffer
ShorBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBufferii.缓冲区存取数据的两个方法
put():存入数据到缓冲区
get():获取缓冲区中的数据iii.缓冲区中的四个核心属性
mark:标记,表示记录当前 position 的位置,可以通过reset 恢复到mark 记录的位置
position:位置,表示缓冲区中正在操作数据的位置
limit:界限,表示缓冲区可以操作数据的大小(limit 后面的数据不能进行读写)
capacity:容量,标书缓冲区中最大存储数据的容量,一旦申明不能改变
(0 ≤ mark ≤ position ≤ limit ≤ capacity)iv.其他方法
flip() :切换读取数据的模式
rewind ():可重复读数据
clear (): 清空缓冲区但是缓冲区的数据依然存在,但是处于被遗忘状态,不能正确的读取数据v.直接缓冲区和非直接缓冲区
1.非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中(堆内存开辟工作区间,数组)
2.直接缓冲区:通过allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率
缺点:物理内存映射文件中的内容不归我们管,要开辟物理内存,gc 回收
isDirect() 方法判断是否是直接缓冲区 isDirect()
b) 通道(channel):专门用IO操作
i. 通道与流的区别
通道(channel):用于源节点和目标节点的链接,在Java NIO 中负责缓冲区中数据的传输,通道本身不存储任何数据,需要配合缓冲区进行传输
ii. 通道的主要实现类:
java.nio.channels.Channel 接口
|–FileChannel(本地数据的传输)
|–SocketChannel(网络数据的传输)
|–ServerSocketChannel(网络数据的传输)
|–DatagramChannel(网络数据的传输)
iii. 获取通道的三种方式:
1. Java 针对支持通道的类提供了getChannel() 方法
本地IO:
FileInputStream/FileOutputStream
RandomAccessFile
网络IO:
Socket
ServerSocket (TCP)
DatagramSocket(UDP)
- JDK1.7 中的 NIO.2针对各个通道提供了一个静态方法 open()
- 在JDK1.7 中的NIO.2的 Files 工具类的newByteChannel()
iv. 通道之间的数据传输(直接缓冲区的方式)
transferFrom()
transferTo ()
v. 示例
1. 利用通道完成文件的复制
FileInputStream fis = null;FileOutputStream fos = null;FileChannel inChannel = null;FileChannel outChannel = null;try { fis = new FileInputStream("1.jpg"); fos = new FileOutputStream("2.jpg"); // ①获取通道 inChannel = fis.getChannel(); outChannel = fos.getChannel(); // ②分配指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); // ③将通道中的数据存入缓冲区 while(inChannel.read(buf) != -1){ buf.flip(); // 切换成读取数据模式 outChannel.write(buf); // ④ 将缓冲区的数据写入通道 buf.clear(); // 清空缓冲区 }} catch (FileNotFoundException e) { e.printStackTrace();} catch (IOException e) { e.printStackTrace();}finally { if(outChannel !=null){ try { outChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(inChannel !=null){ try { inChannel.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos !=null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis !=null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } }}
- 只用直接缓冲区完成文件的复制(内存映射文件方式)(直接缓冲区的模式只有 ByteBuffer 支持)
@Testpublic void test2() throws IOException{ Instant start = Instant.now(); FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); // 内存映射文件 MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); // 直接对缓冲区进行数据的读写操作 byte[] dst = new byte[inMappedBuf.limit()]; inMappedBuf.get(dst); outMappedBuf.put(dst); inChannel.close(); outChannel.close(); Instant end = Instant.now(); System.out.println(Duration.between(start, end).toMillis()); }
- 通道之间的数据传输
@Testpublic void test3() throws IOException{ FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); // inChannel.transferTo(0, inChannel.size(), outChannel); outChannel.transferFrom(inChannel, 0, inChannel.size());}
vi. 分散(scatter)与聚集(gather)
分散读取(scattering reads): 将通道中的数据分散到多个缓冲区中
聚集写入(gathering writes): 将多个缓冲区的数据聚集到通道中
//分散聚集@Testpublic void test4() throws IOException{ RandomAccessFile raf1 = new RandomAccessFile("test.txt", "rw"); // 获取通道 FileChannel channel = raf1.getChannel(); // 分配指定大小的缓冲区 ByteBuffer buf1 = ByteBuffer.allocate(1024); ByteBuffer buf2 = ByteBuffer.allocate(1024); // 分散读取 ByteBuffer[] bufs = {buf1,buf2}; channel.read(bufs); for (ByteBuffer byteBuffer : bufs) { byteBuffer.flip(); } System.out.println(new String(bufs[0].array(),0,bufs[0].limit()) ); System.out.println("***********************************") ; System.out.println(new String(bufs[1].array(),0,bufs[1].limit()) ); // 具体写入 RandomAccessFile raf2 = new RandomAccessFile("test2.txt", "rw"); FileChannel channel2 = raf2.getChannel(); channel2.write(bufs);}
vvi. 字符集 charset
编码:字符串—》字节数组
解码:字节数组—》字符串
@Testpublic void test6() throws IOException{ Charset cs1 = Charset.forName("GBK"); // 获取编码器 CharsetEncoder encoder = cs1.newEncoder(); // 获取解码器 CharsetDecoder decoder = cs1.newDecoder(); CharBuffer cbuf = CharBuffer.allocate(1024); cbuf.put("my test!"); cbuf.flip(); //编码 ByteBuffer bBuf = encoder.encode(cbuf); for (int i = 0; i <12; i++) { System.out.println(bBuf.get()); } //解码 bBuf.flip(); CharBuffer ccbuf = decoder.decode(bBuf); System.out.println(ccbuf.toString()); bBuf.flip(); // GBK 编码 utf-8 解码出现乱码 Charset cs2 = Charset.forName("UTF-8"); CharBuffer cbbuf = cs2.decode(bBuf); System.out.println(cbbuf.toString());}
- NIO 的非阻塞式网络通信
a) 使用NIO 完成网络通信的三个核心
// 客户端@Testpublic void client() throws IOException{ // 1、获取通道 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9803)); // 2、切换成非阻塞模式 socketChannel.configureBlocking(false); // 3、分配指定大小的缓冲区 ByteBuffer buf = ByteBuffer.allocate(1024); // 4、读取本地文件并发送到服务器 buf.put(new Date().toString().getBytes()); buf.flip(); socketChannel.write(buf); buf.clear(); // 4、关闭通道 socketChannel.close();}
// 服务端 @Test public void server() throws IOException{ // 1、获取通道 ServerSocketChannel ssChannel = ServerSocketChannel.open(); // 2、切换非阻塞模式 ssChannel.configureBlocking(false); // 3、连接绑定 ssChannel.bind(new InetSocketAddress(9803)); // 4、获取选择器 Selector selector = Selector.open(); // 5、将通道注册到选择器上,并且指定监听事件 ssChannel.register(selector, SelectionKey.OP_ACCEPT); // 6、轮询式的获取选择器上已经“准备就绪”的时间 while(selector.select() > 0){ // 7、获取当前选择器中多有注册的选择键(已注册的监听事件) Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while(iterator.hasNext()){ // 8、迭代获取准备就绪的事件 SelectionKey sk = iterator.next(); // 9、判断具体是什么事件准备就绪 if(sk.isAcceptable()){ // 10、若"接收就绪" ,获取客户端链接 SocketChannel socketChannel = ssChannel.accept(); // 11、切换非阻塞模式 socketChannel.configureBlocking(false); // 12、将该通道注册到选择器 socketChannel.register(selector, SelectionKey.OP_ACCEPT); }else if(sk.isReadable()){ // 13、获取当前选择器上读就绪状态的通道 SocketChannel channel = (SocketChannel) sk.channel(); // 14、读取数据 ByteBuffer buf = ByteBuffer.allocate(1024); int len = 0; while((len = channel.read(buf)) > 0){ buf.flip(); System.out.println(new String(buf.array(), 0, len)); buf.clear(); } } // 15、取消选择键 iterator.remove(); } } }
- nio
- NIO
- NIO
- nio
- NIO
- NIO
- nio
- Nio
- NIO
- NIO
- NIO
- nio
- NIO
- NIO
- NIO
- NIO
- NIO
- NIO
- [ubuntu] 修改 ubuntu 16.04 开机默认启动项
- 贪心算法
- 关于一棵树
- SSL P2676 数学math
- JSON
- NIO
- Git工作区&暂存区&版本库(“三巨头”)、删除文件、撤销操作
- 排序算法的个人理解
- sqlserver实现层级树形查询(第二弹)
- JavaScript(五)高阶函数sort
- SSL P2677 飞行fly
- Yii2 Form表单样式修改
- git怎么查看哪些文件是在版本控制下的呢
- [ZJOI2004]沼泽鳄鱼(矩阵乘法)