1.NIO 和 IO 区别
首先传统的 io 是面向流 Stream Oriented,单向; 阻塞 io
NIO 是面向缓冲区 Buffer Oriented 双向; 非阻塞 Io;有选择器;
2.NIO
NIO 使用时通过通道和缓冲区进行同时工作的;可以将通道理解成火车到,缓冲区是火车;
1)缓冲区:实质是数据,用来存储的
除了 boolean 以外其他的都具有相对应的缓冲区;获得缓冲区 XXXBuffer.allocate(int capacity);
缓冲区有四个属性;
- 1.mark 用来标记当前位置的;可通过 reset()方法回到标记位置
- 2.position 当前位置,起始位置是 0,
- 3.limit 界限
- 4.capacity 容量
存取方法 put() get() 每次在读取的时候我们要进行 flip()切换到读模式;
直接缓冲区和非直接缓冲区
直接缓冲区是直接在物理内存上进行存储;优点是非常快,缺点不受控制,放在物理内存上什么时候放在物理磁盘就不受程序控制;
直接缓冲区的获取:
1、static ByteBuffer allocateDirect(int)
2、FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回 MappedByteBuffer
2)通道:用来传输的;他是一个独立的控制器
获取通道的方法:
-
1. 通过 getChannel()方法获得;获取本地的 FileInputStream/FileOutputStream;RandomAccessFile , 获取网络的 serverSocket ,datagramSocket;
// 将 1 复制到 2 上,那么我们首先要通过 try {FileInputStream is = new FileInputStream("1.jpg"); FileOutputStream os = new FileOutputStream("2.jpg"); // 得到通道 FileChannel isChannel = is.getChannel(); FileChannel osChannel = os.getChannel(); // 通过缓冲区将进行复制 ByteBuffer bf = ByteBuffer.allocate(1024);// 相当于原来的字节数组;// 读取通道并且写入 while(isChannel.read(bf)!=-1){bf.flip();osChannel.write(bf); bf.clear();} osChannel.close(); isChannel.close(); os.close(); is.close();} catch (IOException e) {e.printStackTrace(); } finally {}
-
2. 支持通道静态方法 Open(); filechanne;socketchanne;serversocketchannel;datagramchannel
public static void main(String[] args) { // 2 使用直接内存方式复制; ①使用内存映射文件;try {FileChannel is = FileChannel.open(Paths.get("d:/1.jpg"), StandardOpenOption.READ); FileChannel os = FileChannel.open(Paths.get("d:/2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW); // 从内存映射文件中获取;MappedByteBuffer isMap = is.map(FileChannel.MapMode.READ_ONLY, 0, is.size()); MappedByteBuffer osMap = os.map(FileChannel.MapMode.READ_WRITE, 0, is.size()); // 直接对缓冲区数据进行操作 byte[] bytes = new byte[isMap.limit()]; isMap.get(bytes); osMap.put(bytes); is.close(); os.close();} catch (Exception e) {e.printStackTrace(); } finally {}}
public static void main(String[] args) {
// 2 使用直接内存方式复制; ②使用通道
try {FileChannel is = FileChannel.open(Paths.get("d:/1.jpg"), StandardOpenOption.READ);
FileChannel os = FileChannel.open(Paths.get("d:/2.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);
// is.transferTo(0,is.size(),os);
os.transferFrom(is,0,is.size());
is.close();
os.close();} catch (Exception e) {e.printStackTrace();
} finally {}}
分散与聚集
分散读取:用多个缓冲区去读取
聚集写入:将多个缓冲区的数据写入到一个通道
public static void main(String[] args) {
try {RandomAccessFile rw = new RandomAccessFile("1.txt", "rw");
// 得到了通道
FileChannel channel = rw.getChannel();
// 继续获得缓冲区
ByteBuffer by1 = ByteBuffer.allocate(100);
ByteBuffer by2 = ByteBuffer.allocate(1024);
// 分散读取 多个缓冲区去读取
ByteBuffer[] bfs = {by1,by2};
channel.read(bfs);
for(ByteBuffer b :bfs){b.flip();
}
// 聚集写入
RandomAccessFile rw2 = new RandomAccessFile("2.txt", "rw");
FileChannel channel1 = rw2.getChannel();
channel1.write(bfs);
channel.close();
rw.close();} catch (Exception e) {e.printStackTrace();
}
}
NIO 选择器
传统的线程为了避免客户端和服务器请求阻塞,采用的是多线程的方式,而现在 NIO 采用的就是选择器
将通道注册到选择器上,选择器换监视发送过来的请求是否准备就绪,准备就绪了才会将他分配到一个或多个线程上;
filechannel 不能切换成非阻塞式
selectableChannel 下 1.socketchannel,2.serversocketchannel–tcp 3.datagramchannel;—udp
还可以用于检测管段 pipe.sinkchannel;pipe.sourcechannel;
传统的阻塞式
public class NIOBlocking {
@Test
public void client() throws IOException {
//1. 获取通道
SocketChannel open = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
FileChannel open1 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
//2. 获得缓冲区
ByteBuffer bbf = ByteBuffer.allocate(1024);
//3. 读取本地文件发送到服务器
while(open1.read(bbf)!=-1){bbf.flip();
open.write(bbf);
bbf.clear();}
// 关闭流
open1.close();
open.close();}
@Test
public void server() throws IOException {
//1. 获取通道
ServerSocketChannel open = ServerSocketChannel.open();
// 为了保存到本地
FileChannel open1 = FileChannel.open(Paths.get("3.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//2. 绑定连接
open.bind(new InetSocketAddress(8080));
//3. 获取客户端连接的通道
SocketChannel accept = open.accept();
//4. 获取指定大小的缓冲区
ByteBuffer bbf = ByteBuffer.allocate(1024);
//5. 接收客户端的数据保存到本地;while(accept.read(bbf)!=-1){bbf.flip();
open1.write(bbf);
bbf.clear();}
// 关闭通道
accept.close();
open1.close();
open.close();}
}
非阻塞式,也即是选择器式
public class NonBlocking {
@Test
public void client() throws IOException {
//1. 获取通道
SocketChannel open = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
//2. 切换非阻塞
open.configureBlocking(false);
//3. 获取缓冲区
ByteBuffer bbf = ByteBuffer.allocate(1024);
//4. 向服务器发送数据
bbf.put(new Date().toString().getBytes());
bbf.flip();
open.write(bbf);
bbf.clear();
//5. 关闭通道
open.close();}
@Test
public void server() throws IOException {
//1. 获取通道
ServerSocketChannel open = ServerSocketChannel.open();
//2. 切换成非阻塞模式
open.configureBlocking(false);
//3. 绑定连接
open.bind(new InetSocketAddress(8080));
//4. 获取选择器 -- 将通道注册到选择器上;---- 循环比那里看选择器上是否有准备就绪事件
Selector selector = Selector.open();
open.register(selector, SelectionKey.OP_CONNECT);
while(selector.select()>0){
//5. 获取选择器上所有注册的选择键(已经准备就绪的监听事件)Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while(it.hasNext()){SelectionKey sk = it.next();
// 6 判断我们得到的 selectionkey 是不是就绪
if(sk.isAcceptable()){
// 7 如果是接收就绪 获得客户端通道 设置成 false; 将该通道注册的到选择器设置成读
SocketChannel accept = open.accept();
accept.configureBlocking(false);
accept.register(selector,SelectionKey.OP_READ);
}else if(sk.isReadable()){
// 8 读就绪 获取通道
SocketChannel channel = (SocketChannel)sk.channel();
// 9 有通到了,我们想要进行读还需要缓存区
ByteBuffer bbf = ByteBuffer.allocate(1024);
int len=0;
while((len=channel.read(bbf))!=-1){bbf.flip();
System.out.println(new String(bbf.array(),0,len));
bbf.clear();}
}
//10 关闭通道
it.remove();}
}
}
}
datagramchannel 同理;就不在这里说了
pipe 管道是连接两个线程之间的单项数据连接;sink 写;source 读;我们可以放在两个线程上一个写,一个读;
@Test
public void test() throws IOException {
// 得到通道
Pipe pipe = Pipe.open();
// 获取缓冲区
ByteBuffer bbf = ByteBuffer.allocate(1024);
bbf.put("马尾与小丑".getBytes());
// 向通道写数据
Pipe.SinkChannel sink = pipe.sink();
bbf.flip();
sink.write(bbf);
//---------------------- 上边可以放在一个线程,下边可以放在一个下线程;// 读数据
Pipe.SourceChannel source = pipe.source();
bbf.flip();
int len=source.read(bbf);
System.out.println(new String(bbf.array(),0,len));
source.close();
sink.close();}