• 阻塞IO

  传统的 IO 流都是阻塞式的。

  也就是说,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务。

  因此,在完成网络通信进行 IO 操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。

  注意:在阻塞IO操作的过程中,用来提高程序的解决方案一般是使用多线程来处理,但是开辟线程也是比较耗费资源的。

测试NIO阻塞模式:

 @Test
public void client() throws IOException {
// 1、获取通道(channel)
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
FileChannel inChannel = FileChannel.open(Paths.get("Java NIO.pdf"),StandardOpenOption.READ); // 2、分配指定大小的缓冲区
ByteBuffer byteBuffer=ByteBuffer.allocate(1024); // 3、读取本地文件,并写入发送channel
while (inChannel.read(byteBuffer)!=-1) {
byteBuffer.flip();// 切换到读模式
socketChannel.write(byteBuffer);
byteBuffer.clear();// 清空缓冲区
} // 必须shutdown否则就没法切换到接收数据的模式
socketChannel.shutdownOutput(); System.out.println("client waiting reading server response");
// 接收服务端的数据
int length=0;
while((length=socketChannel.read(byteBuffer))!=-1){
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,length));
byteBuffer.clear();
} System.out.println("end...");
inChannel.close();
socketChannel.close();
} @Test
public void server() throws IOException{
// 1、获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
FileChannel outChannel=FileChannel.open(Paths.get("33.pdf"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE); // 2、绑定连接
serverSocketChannel.bind(new InetSocketAddress(9898));
// 3、获取客户端的连接
SocketChannel accept = serverSocketChannel.accept(); // 4、分配指定大小的缓冲区
ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
// 5、接收客户端的数据,并保存到本地
while (accept.read(byteBuffer)!=-1) {
byteBuffer.flip();
outChannel.write(byteBuffer);
byteBuffer.clear();
} System.out.println("server print ..."); byteBuffer.put("server success".getBytes());
byteBuffer.flip();//切换到读模式
accept.write(byteBuffer); // 6、关闭连接
accept.close();
outChannel.close();
serverSocketChannel.close();
}

打印结果:

client waiting reading server response
server success
end...

  • 非阻塞

  Java NIO 是非阻塞模式的。

  当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞 IO 的空闲时间用于在其他通道上执行 IO 操作,所以单独的线程可以管理多个输入和输出通道。

  因此, NIO 可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。

  • 如何形成非阻塞IO:

从上边的图中我们知道要构成NIO非阻塞模式,必须要引入Selector。那么,什么是Selector?

  • 选择器(Selector)

选择器(Selector)是SelectableChannle对象的多路复用器,Selector可以同时监控多个SelectableChannel的IO状况,也就是说,利用Selector可以一个单独的线程管理多个Channel。Selector是非阻塞IO的核心。

  • 使用NIO实现网络通信的三个核心:

1、通道(channel):负责连接

java.nio.channels.Channel接口:
  |--SelectableChannel
    |--SocketChannel
    |--ServerSocketChannel
    |--DatagramChannel

    |--Pipe.SinkChannel
    |--Pipe.SourceChannel
2、缓冲区(Buffer):负责数据的存储
3、选择器(Selector):是SelectableChannel的多路复用器。用于监控SelectableChannel的IO状况。

  • 非阻塞IO示例:
     /**
* 客户端
*/
@Test
public void client() throws IOException {
// 1、获取通道(channel)
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
// 2、切换成非阻塞模式
socketChannel.configureBlocking(false); // 3、分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("你可理论上的 。。。".getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer); socketChannel.close();
} @Test
public void server() throws IOException {
// 1、获取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2.设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 3、绑定连接
serverSocketChannel.bind(new InetSocketAddress(9898)); // 4、获取Selector选择器
Selector selector = Selector.open(); // 5、将通道注册到选择器上,并制定监听事件为:“接收”事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 6、采用轮询的方式获取选择器上“准备就绪”的任务
while (selector.select() > 0) {
// 7、获取当前选择器中所有注册的选择键(“已经准备就绪的事件”)
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
// 8、获取“准备就绪”的时间
SelectionKey selectedKey = selectedKeys.next(); // 9、判断key是具体的什么事件
if (selectedKey.isAcceptable()) {
// 10、若接受的事件是“接收就绪”事件,就获取客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
// 11、切换为非阻塞模式
socketChannel.configureBlocking(false);
// 12、将该通道注册到selector选择器上
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectedKey.isReadable()) {
// 13、获取该选择器上的“读就绪”状态的通道
SocketChannel socketChannel = (SocketChannel) selectedKey.channel(); // 14、读取数据
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int length = 0;
while ((length = socketChannel.read(byteBuffer)) != -1) {
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(), 0, length));
byteBuffer.clear();
}
socketChannel.close();
} // 15、移除选择键
selectedKeys.remove();
}
} // 7、关闭连接
serverSocketChannel.close();
}

Java-NIO(七):阻塞IO与非阻塞IO的更多相关文章

  1. 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?

    原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...

  2. NIO之阻塞IO与非阻塞IO(包含Selector使用)

    阻塞IO 传统的 IO 流都是阻塞式的. 也就是说,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有一些数据被读取或写入,该线程在此期间不能执行其他任务. 因此,在完成网络通信 ...

  3. 【死磕NIO】— 阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,这你真的分的清楚吗?

    通过上篇文章([死磕NIO]- 阻塞.非阻塞.同步.异步,傻傻分不清楚),我想你应该能够区分了什么是阻塞.非阻塞.异步.非异步了,这篇文章我们来彻底弄清楚什么是阻塞IO,非阻塞IO,IO复用,信号驱动 ...

  4. 阻塞式和非阻塞式IO

    有很多人把阻塞认为是同步,把非阻塞认为是异步:个人认为这样是不准确的,当然从思想上可以这样类比,但方式是完全不同的,下面说说在JAVA里面阻塞IO和非阻塞IO的区别 在JDK1.4中引入了一个NIO的 ...

  5. 深入理解非阻塞同步IO和非阻塞异步IO

    这两篇文章分析了Linux下的5种IO模型 http://blog.csdn.net/historyasamirror/article/details/5778378 http://blog.csdn ...

  6. 阻塞IO,非阻塞IO,异步IO和非异步IO 的区别

    最近在研究java IO.NIO.NIO2(或者称AIO)相关的东西,有些概念还是要明确下. 按照<Unix网络编程>的划分,IO模型可以分为:阻塞IO.非阻塞IO.IO复用.信号驱动IO ...

  7. 5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO

    POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. ...

  8. 阻塞IO和非阻塞IO的区别

    转载地址: http://blog.sina.com.cn/s/blog_a46817ff0101g0gv.html http://blog.csdn.net/nodeathphoenix/artic ...

  9. 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出 ...

  10. 转 网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    此文章为转载,如有侵权,请联系本人.转载出处,http://blog.chinaunix.net/uid-28458801-id-4464639.html 同步(synchronous) IO和异步( ...

随机推荐

  1. UML系列图2

    时序图: 用例图:

  2. java反射机制(先马再看)

    http://blog.csdn.net/sinat_38259539/article/details/71799078

  3. paho.mqtt.c打印日志

    mqtt中自身就带有日志系统Log.h和Log.c,这些日志文件是在客户端调用MQTTClient_create函数是初始化的,MQTTClient_create源码如下: int MQTTClien ...

  4. scanf()中的%c 不能正常输入的问题

    #include <stdio.h> int main() { char a; int b; scanf("%d",&b); scanf("%c&qu ...

  5. Redis分布式锁---完美实现

    这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...

  6. 大数据 --> 一致性Hash算法

    一致性Hash算法 一致性Hash算法(Consistent Hash)

  7. 设计模式 --> (4)建造者模式

    建造者(Builder)模式 建造者(Builder)模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 建造者模式包含一个抽象的Builder类,还有它的若干子类——Co ...

  8. ThoughtWorks.QRCode 生成QR二维码时提示“索引超出了数组界限”的原因和解决方法

    "索引超出了数组界限"也有可能确实是因为你选择的二维码Version对应的容量不足以存储你所放的内容,如果你确定使用的版本容量二维码能存储你的内容,但还是报错,那么再考虑此解决方法 ...

  9. 设计模式之 原型模式详解(clone方法源码的简单剖析)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...

  10. mqtt异步publish方法

    Python基于mqtt异步编程主要用到asyncio及第三方库hbmqtt,这里主要介绍mqtt的异步发布及遇到的一些问题. hbmqtt安装很简单,pip hbmqtt install. mqtt ...