1 非阻塞NIO和阻塞IO
1.1 定义
阻塞IO:线程被阻塞,去处理一个读取和写入,中间如果有等待时间,则线程被占用,也不能处理其他任务;
非阻塞IO(new IO):引入了通道的概念,一个连接对应一个通道,为每个通道配置缓冲区,线程去轮询查看的通道的状态,如果某个通道上有数据准备好了,则通道状态会发生改变,线程就去处理这个读写操作;
1.2 非阻塞实现的核心Selector
Selector 一般称为选择器 ,或者多路复用器。在通道上建立socket连接,然后调用channel.register(selector, Selectionkey.OP_READ);将通道注册到Selector上,并且申明这个连接是读(READ)、写(WRITE)、接受(ACCEPT)、连接( CONNECT),或者四个功能都包括;这样就可以用一个线程,一个Selector去管理多个连接;selector会去循环查询各个通道的状态,如果通道状态发生改变,会去判断通道的类型(读、写、接受、连接),然后再对应去做处理;
(1)Selector的创建
通过调用Selector.open()方法创建一个Selector对象,如下:
Selector selector = Selector.open();
(2)注册Channel到Selector
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
第二个参数是通道的类型,有四个值,方便位运算;
读 : SelectionKey.OP_READ ( 1)
写 : SelectionKey.OP_WRITE ( 4)
连接 : SelectionKey.OP_CONNECT ( 8)
接收 : SelectionKey.OP_ACCEPT ( 16)
若注册时不止监听一个事件,则可以使用“位或”操作符连接。
1.3 SelectableChannle通道上建立连接
(1)非阻塞通道
AbstractSelectableChannel抽象类继承了SelectableChannle接口, SocketChannel、ServerSocketChannel、DatagramChannel都是直接继承了 AbstractSelectableChannel抽象类 。SocketChannel是用于socket客户端,ServerSocketChannel是用于socket服务器;
(2)阻塞通道
FileChannel还是不能实现非阻塞,对文件的读写IO,不能同时写入一个文件。
1.4 SelectionKey通道状态
SelectionKey 代表各个通道的状态信号,通过调用选择器的函数来获取到所有有信号的通道的状态,Set<SelectionKey> keys = selector.selectedKeys();然后在通过SelectionKey的方法去判断是哪种信号。
(1)key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
(2)key.channel(); // 返回该SelectionKey对应的channel。
(3)key.selector(); // 返回该SelectionKey对应的Selector。
(4)key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask
我们可以通过与操作来判断Selector是否对Channel的某种事件感兴趣,感兴趣的boolean为1;
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
(5)key.readyOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。可以通过与运算去判断是否准备好;
(6)直接调用SelectionKey对象的函数判断信号状态
boolean isReadable() 检测 Channal 中读事件是否就绪
boolean isWritable() 检测 Channal 中写事件是否就绪
boolean isConnectable() 检测 Channel 中连接是否就绪
boolean isAcceptable() 检测 Channel 中接收是否就绪
1.5 阻塞IO使用实例
客户端向服务器端发送一张图片
package com.happybks.nio.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
importjava.nio.
file.Paths;
importjava.nio.
file.StandardOpenOption;
import org.junit.Test;
publicclass TestBlockingNIO
{
//客户端
@Test
public void client() throws IOException
{
//1、获取通道(open这种方法是jdk1.7之后才引入的)
SocketChannel socketChannel = SocketChannel.open(
newInetSocketAddress(
"127.0.0.1",
8888));
//2、分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(
1024);
//3、从本地读取文件,并发送到服务端
FileChannel inFileChannel=FileChannel.open(Paths.get(
"D:/Test/NIO/1.jpg"),
StandardOpenOption.
READ);
while
(inFileChannel.
read(buf)!=-
1){
buf.flip();
socketChannel.
write(buf);
buf.clear();
}
//在阻塞IO下,如果关闭socketChannel,那么服务端不知道客户端是否已经把所有数据发完,所以会一直阻塞。
socketChannel.shutdownOutput();
//另一种方法就是把这个线程切换成非阻塞模式
//接收服务端反馈
int
len =
0;
while
((len = socketChannel.
read(buf)) !=-
1){
buf.flip();
System.out.
println(
newString(buf.array(),
0,len));
}
//4、关闭通道
inFileChannel.close();
socketChannel.close();
}
//服务端
@Test
public void server() throws IOException
{
//1、获取端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2、绑定连接
serverSocketChannel.bind(
newInetSocketAddress(
8888));
//3、获取客户端连接的通道
SocketChannel socketChannel = serverSocketChannel.accept();
//4、接收客户端的数据,保存到本地。(提到本地,就要弄个FileChannel)
FileChannel outFileChannel = FileChannel.open(Paths.get(
"D:/Test/NIO/2.jpg"),
StandardOpenOption.
WRITE,StandardOpenOption.CREATE);
ByteBuffer buf=ByteBuffer.allocate(
1024);
while
(socketChannel.
read(buf)!=-
1){
buf.flip();
outFileChannel.
write(buf);
buf.clear();
}
//发送反馈给客户端
buf.put(
"服务端接收数据成功!".getBytes());
buf.flip();
socketChannel.
write(buf);
socketChannel.close();
outFileChannel.close();
serverSocketChannel.close();
}
}
1.6 非阻塞IO使用实例
package com.happybks.nio.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.util.Date;
import java.util.Iterator;
import org.junit.Test;
public class TestNonBlockingNIO {
//客户端
@Test
public void client() throws IOException{
//1、获取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
//2、切换非阻塞模式
socketChannel.configureBlocking(false);
//3、分配指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024);
//4、发送数据服务器
buf.put(new Date().toString().getBytes());
buf.flip();
socketChannel.write(buf);
buf.clear();
//5、关闭通道
socketChannel.close();
}
//服务端
@Test
public void server() throws IOException{
//1、获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2、切换非阻塞模式
serverSocketChannel.configureBlocking(false);
//3、绑定连接
serverSocketChannel.bind(new InetSocketAddress(8888));
//4、获取选择器
Selector selector = Selector.open();
//5、将通道注册到选择器上(第二个选项参数叫做选择键,用于告诉选择器需要监控这个通道的什么状态或者说什么事件(读、写、连接、接受))
//选择键是整型值,如果需要监控该通道的多个状态或事件,可以将多个选择键用位运算符“或”“|”来连接
//这里服务端首先要监听客户端的接受状态
serverSocketChannel.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 = serverSocketChannel.accept();
//11、客户端连接socketChannel也需要切换非阻塞模式
socketChannel.configureBlocking(false);
//12、将该通道注册到选择器上,监控客户端socketChannel的读就绪事件
socketChannel.register(selector, SelectionKey.OP_READ);
}
else if(sk.isReadable()){
//13、获取当前选择器上“读就绪”状态的通道
SocketChannel socketChannel = (SocketChannel) sk.channel();
//14、读取数据
ByteBuffer buf=ByteBuffer.allocate(1024);
int len=0;
while((len=socketChannel.read(buf))>0){
buf.flip();
System.out.println(new String(buf.array(),0,len));
buf.clear();
}
}
//15、取消选择键SelectionKey
iterator.remove();
}
}
}
}
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
https://www.cnblogs.com/bclshuai/p/11380657.html
百度云盘下载地址:
链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg
提取码:mc8l
微信公众号获取最新的软件和视频介绍
QStockView