前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

NIO

作者头像
用户5927264
发布2019-10-15 10:40:16
7160
发布2019-10-15 10:40:16
举报
文章被收录于专栏:OSChina

详细知识参考我有道云笔记

代码语言:javascript
复制
package com.shi.nio;

import java.nio.ByteBuffer;

/**
 * 
 * @author shiye
 *	一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 * 
 *	 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
 * ByteBuffer
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 * 
 * ·上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
 * 
 *	 二、缓冲区存取数据的两个核心方法:
 * put() : 存入数据到缓冲区中
 * get() : 获取缓冲区中的数据
 * 
 * 	三、缓冲区中的四个核心属性:
 * capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
 * limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
 * position : 位置,表示缓冲区中正在操作数据的位置。
 * 
 * mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
 * 
 * 0 <= mark <= position <= limit <= capacity
 * 
 * 	四、直接缓冲区与非直接缓冲区:
 * 	非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
 * 	直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
 */
public class TestBuffer {

	public static void main(String[] args) {
		String str = "shiye";
		//1 分配一个指定大小的缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(1024); 
//		ByteBuffer.allocateDirect(10);//直接内存缓冲区
//		buffer.isDirect();//是否是直接内存缓冲区
		
		System.out.println("-------------------1 allocate(初始创建)-----------");
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		buffer.put(str.getBytes());
		
		System.out.println("-------------------2 put(写数据)-----------");
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		buffer.flip();
		System.out.println("-------------------3 flip(切换到读取数据模式)-----------");
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		
		System.out.println("-------------------4 get(读取数据)-----------");
		
		byte[] dst = new byte[10];
		buffer.get(dst, 0, 2);//从下标0,读取2个数据 道dst中
		System.out.println("dst中读到的数据位:"+new String(dst));
		
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		buffer.rewind();
		System.out.println("-------------------5 rewind(重新读数据)-----------");
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		System.out.println("buffer中读到的数据位:"+(char)buffer.get());//rewind 测试
		
		buffer.clear();
		System.out.println("-------------------6 clear(清除位置信息,数据还存在)-----------");
		System.out.println("容量:"+buffer.capacity());
		System.out.println("界限:"+buffer.limit());
		System.out.println("当前位置:"+buffer.position());
		
		System.out.println("buffer中读到的数据位:"+(char)buffer.get());//clear 测试
		
		
		System.out.println("-------------------7 mark(标记当前位置)-----------");
		System.out.println("当前位置:"+buffer.position());
		buffer.mark();//mark 标记当前位置
		System.out.println("buffer中读到的数据位:"+(char)buffer.get());//mark 测试
		System.out.println("当前位置:"+buffer.position());
		
		buffer.reset();//返回到mark标记好的位置 重新读取数据
		System.out.println("reset当前位置:"+buffer.position());//reset 测试
		
		
		if(buffer.hasRemaining()) {
			System.out.println("当前缓冲区中剩余可以操作的的数据的数量"+buffer.remaining());
		}
	}

	/**
	运行结果:
	-------------------1 allocate(初始创建)-----------
	容量:1024
	界限:1024
	当前位置:0
	-------------------2 put(写数据)-----------
	容量:1024
	界限:1024
	当前位置:5
	-------------------3 flip(切换到读取数据模式)-----------
	容量:1024
	界限:5
	当前位置:0
	-------------------4 get(读取数据)-----------
	dst中读到的数据位:sh
	容量:1024
	界限:5
	当前位置:2
	-------------------5 rewind(重新读数据)-----------
	容量:1024
	界限:5
	当前位置:0
	buffer中读到的数据位:s
	-------------------6 clear(清除位置信息,数据还存在)-----------
	容量:1024
	界限:1024
	当前位置:0
	buffer中读到的数据位:s
	-------------------7 mark(标记当前位置)-----------
	当前位置:1
	buffer中读到的数据位:h
	当前位置:2
	reset当前位置:1
	当前缓冲区中剩余可以操作的的数据的数量1023
	 */
}
代码语言:javascript
复制
package com.shi.nio;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.LinkedList;
import java.util.List;

/**
 * 
 * @author shiye 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel
 *         本身不存储数据,因此需要配合缓冲区进行传输。
 * 
 *         二、通道的主要实现类 java.nio.channels.Channel 接口: |--FileChannel
 *         |--SocketChannel |--ServerSocketChannel |--DatagramChannel
 * 
 *         三、获取通道 1. Java 针对支持通道的类提供了 getChannel() 方法 本地 IO:
 *         FileInputStream/FileOutputStream RandomAccessFile
 * 
 *         网络IO: Socket ServerSocket DatagramSocket
 * 
 *         2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open() 3. 在 JDK 1.7 中的 NIO.2 的
 *         Files 工具类的 newByteChannel()
 * 
 *         四、通道之间的数据传输 transferFrom() transferTo()
 * 
 *         五、分散(Scatter)与聚集(Gather) 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 *         聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 * 
 *         六、字符集:Charset 编码:字符串 -> 字节数组 解码:字节数组 -> 字符串
 */
public class TestChannel {

	/**
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		test4();
		
	}
	
	/**
	 * 	编码和解码
	 * @throws CharacterCodingException 
	 */
	public static void test4() throws CharacterCodingException {
		
		//获取UTF-8的字符集
		Charset charset = Charset.forName("UTF-8");
		
		//获取编码器
		CharsetEncoder utf8_encoder = charset.newEncoder();
		
		//获取解码器
		CharsetDecoder utf8_decoder = charset.newDecoder();
		
		CharBuffer charBuffer = CharBuffer.allocate(1024);
		charBuffer.put("还是施爷足智多谋!");
		charBuffer.flip();
		
		//1 编码 CharBuffer -> ByteBuffer 字符流->字节流
		ByteBuffer byteBuffer = utf8_encoder.encode(charBuffer);
		for(int i = 0;i<byteBuffer.limit();i++) {
			System.out.println(byteBuffer.get());
		}
		
		
		//2 解码 ByteBuffer-> CharBuffer  字节流->字符流
		byteBuffer.flip();
		CharBuffer charBuffer2 = utf8_decoder.decode(byteBuffer);
		System.out.println(charBuffer2.toString());
		
		
		//所有支持的字符集
//		SortedMap<String, Charset> charsets = Charset.availableCharsets();
//		charsets.forEach((key,value)->{
//			System.out.println( key +" + "+value);
//		});
	}
	
	

	/**
	 * 	分散(Scatter)与聚集(Gather) 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中 聚集写入(Gathering
	 * Writes):将多个缓冲区中的数据聚集到通道中
	 * 
	 * @throws IOException
	 * 
	 * 	注意文件不能超过内存的大小
	 * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.nio.HeapByteBuffer.<init>(Unknown Source)
	 * 
	 * 	结果:
	 * 	总耗时:7065
	 */
	public static void test3() throws IOException {
		long time1 = System.currentTimeMillis();
		

		// 1 读取文件 获取通道
		RandomAccessFile raf1 = new RandomAccessFile("d:/nio.zip", "rw");
		FileChannel inChannel = raf1.getChannel();

		// 2 计算总共需要多少个缓冲区
		int size = 1024 * 1024 * 100;// 100M大小
		int index = (int) ((raf1.length() - 1) / size + 1);// 文件超出 Integer.MAX_VALUE 需要分开缓存
		List<ByteBuffer> bufs = new LinkedList<ByteBuffer>();
		for (int i = 0; i < index; i++) {
			if (i != index - 1) {
				ByteBuffer team = ByteBuffer.allocate(size);
				bufs.add(team);
			} else {
//				System.out.println((raf1.length() - (size * (index - 1))) / 1024 / 1024 + "M");
				ByteBuffer team = ByteBuffer.allocate((int) (raf1.length() - (size * (index - 1))));
				bufs.add(team);
			}

		}
		
		//3 分散读取
		ByteBuffer[] byteArray = bufs.toArray(new ByteBuffer[index]);
		inChannel.read(byteArray);
		
		for(ByteBuffer byteBuffer:byteArray) {
			byteBuffer.flip();//变成读模式
		}
		
		//4 聚焦写入
		RandomAccessFile raf2 = new RandomAccessFile("d:/test.zip","rw");
		FileChannel outChanel = raf2.getChannel();
		
		outChanel.write(byteArray);
		
		//5 关闭文件
		inChannel.close();
		outChanel.close();
		
		long time2 = System.currentTimeMillis();
		System.out.println("总耗时:" + (time2 - time1));
	}

	/**
	 * transferFrom() transferTo()
	 * 
	 * @throws IOException 注意文件只能有1.98G 太大文件会丢失 结果: 总耗时:2349
	 */
	public static void test2() throws IOException {
		long time1 = System.currentTimeMillis();

		FileChannel inChannel = FileChannel.open(Paths.get("d:/" + "big.zip"), StandardOpenOption.READ);
		FileChannel outChannel = FileChannel.open(Paths.get("d:/" + "2.zip"), StandardOpenOption.READ,
				StandardOpenOption.WRITE, StandardOpenOption.CREATE);

		inChannel.transferTo(0, inChannel.size(), outChannel);

		long time2 = System.currentTimeMillis();
		System.out.println("总耗时:" + (time2 - time1));

		inChannel.close();
		outChannel.close();
	}

	/**
	 * 使用直接缓冲区完成文件的复制(内存映射文件)
	 * 
	 * @throws IOException
	 * 
	 *                     结果: 读取文件耗时:1264 写文件耗时:14845
	 */
	public static void test1() throws IOException {
		long time1 = System.currentTimeMillis();
		// 1 读文件内存
		FileChannel inChannel = FileChannel.open(Paths.get("d:/" + "nio.zip"), StandardOpenOption.READ);
		MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());// inChannel.size()
																								// 不能大于Integer.MAX_VALUE
		byte[] dst = new byte[inMappedBuffer.limit()];
		inMappedBuffer.get(dst);

		long time2 = System.currentTimeMillis();
		System.out.println("读取文件耗时:" + (time2 - time1));

		// 2 写文件到磁盘
		FileChannel outChannel = FileChannel.open(Paths.get("d:/" + "2.zip"), StandardOpenOption.READ,
				StandardOpenOption.WRITE, StandardOpenOption.CREATE);
		MappedByteBuffer outMappedBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
		outMappedBuffer.put(dst);// 直接对缓冲区进行数据的读写

		long time3 = System.currentTimeMillis();
		System.out.println("写文件耗时:" + (time3 - time2));
		inChannel.close();
		outChannel.close();
	}

}
代码语言:javascript
复制
package com.shi.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.Scanner;
import java.util.Set;

import org.junit.Test;

/*
 *  一。 使用NIO完成网络通讯的三个核心
 *  1.通道 (Channel):负责连接
 *  	java.nio.channels.Channel接口
 *  		|--SocketChannel
 *  		|--ServerSocketChannel
 *  		|--DatagramChannel
 *  
 *  		|--Pipe.SinkChannel
 *  		|--Pipe.SourceChannel
 *  
 *  2.缓冲区(buffer):负责数据的存储
 *  
 *  3.选择器(Selector):是SelectableChannel 的多路复用器.用于监控SeclectableChannel 的IO状况
 *  
 *  
 */
public class TestNonBlockingNIO {
	
	//客户端
	@Test
	public void client() throws IOException {
		//1.获取通道
		SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
		
		//2.切换非阻塞模式
		sChannel.configureBlocking(false);
		
		//3.分配指定大小的缓冲区
		ByteBuffer byteBuffer = ByteBuffer.allocate(1*1024);
		
		//4.发送给服务器端数据
		Scanner scan = new Scanner(System.in);
		while(scan.hasNext()) {
			byteBuffer.put((LocalDateTime.now().toString()+"\t"+scan.next()).getBytes());
			byteBuffer.flip();//切换到读模式
			sChannel.write(byteBuffer);
			byteBuffer.clear();
		}
		
		
		//5.关闭通道
		sChannel.close();
	}
	
	//服务器端
	@Test
	public void server() throws Exception {
		//1.获取通道
		ServerSocketChannel ssChannel = ServerSocketChannel.open();
		
		//2.切换非阻塞模式
		ssChannel.configureBlocking(false);
		
		//3.绑定连接
		ssChannel.bind(new InetSocketAddress(9898));
		
		//4.获取选择器
		Selector selector = Selector.open();
		
		//5.将通道注册到选择器上,并且指定“监听接收事件”
		ssChannel.register(selector, SelectionKey.OP_ACCEPT);
		
		
		//6.轮询式的获取选择器上已经“准备就绪”的事件
		while(selector.select()>0) {
			
			//7.获取当前选择器中所有注册的“选择键(已经就绪的监听事件)”
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			
			//8.获取准备“就绪”的事件
			selectedKeys.forEach(selectionKey->{
				
				if(selectionKey.isAcceptable()) {
					try {
						//9."接收就绪"状态事件
						SocketChannel sChannel = ssChannel.accept();
						
						//10.切换非阻塞模式
						sChannel.configureBlocking(false);
						
						//11.将通道注册到选择器上
						sChannel.register(selector, SelectionKey.OP_READ);
						
					} catch (IOException e) {
						e.printStackTrace();
					}
				}else if(selectionKey.isReadable()) {
					//获取选择器上“读就绪”状态的通道
					SocketChannel sChannel = (SocketChannel) selectionKey.channel();
					
					//读取数据
					ByteBuffer buf = ByteBuffer.allocate(1*1024);
					
					int len = 0;
					try {
						while((len = sChannel.read(buf)) > 0) {
							buf.flip();//变成只读模式
							System.out.println(new String(buf.array(),0,len));
							buf.clear();
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
					
					try {
						sChannel.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
				}else if(selectionKey.isConnectable()) {
					System.out.println("连接状态的通道自己处理....");
				}else if(selectionKey.isValid()) {
					System.out.println("验证状态的通道自己处理....");
				}else if(selectionKey.isWritable()) {
					System.out.println("写状态的通道自己处理....");
				}
			});
			
			ssChannel.close();
		}
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档