NIO之阻塞IO与非阻塞IO

  • 阻塞IO

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

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

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

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

测试NIO阻塞模式:

 1 package com.expgiga.NIO;
 2
 3 import java.io.IOException;
 4 import java.net.InetSocketAddress;
 5 import java.nio.ByteBuffer;
 6 import java.nio.channels.FileChannel;
 7 import java.nio.channels.ServerSocketChannel;
 8 import java.nio.channels.SocketChannel;
 9 import java.nio.file.Paths;
10 import java.nio.file.StandardOpenOption;
11
12 public class TestBlockingNIO {
13
14
15     //客户端
16     public void client() throws IOException {
17         //1.获取通道
18
19         SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("123.0.0.1", 9898));
20
21         FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
22         //2.分配指定大小的缓冲区域
23         ByteBuffer buf = ByteBuffer.allocate(1024);
24
25         //3.读取本地文件,并发送到服务端去
26         while (inChannel.read(buf) != -1) {
27             buf.flip();
28             sChannel.write(buf);
29             buf.clear();
30         }
31
32         //4.关闭通道
33         inChannel.close();
34         sChannel.close();
35     }
36
37
38     //服务端
39     public void server() throws IOException {
40         //1.获取通道
41         ServerSocketChannel ssChannel = ServerSocketChannel.open();
42         FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
43
44         //2.绑定连接
45         ssChannel.bind(new InetSocketAddress(9898));
46
47         //3.获取客户端连接的通道
48         SocketChannel sChannel = ssChannel.accept();
49
50         //4.分配一个指定大小的缓冲区
51         ByteBuffer buf = ByteBuffer.allocate(1024);
52
53         //5.接受客户端的数据,保存到本地
54         while (sChannel.read(buf) != -1) {
55             buf.flip();
56             outChannel.write(buf);
57             buf.clear();
58         }
59
60         //6.关闭通道
61         sChannel.close();
62         outChannel.close();
63         ssChannel.close();
64
65     }
66
67 }

 1 @Test
 2     public void client() throws IOException {
 3         // 1、获取通道(channel)
 4         SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
 5         FileChannel inChannel = FileChannel.open(Paths.get("Java NIO.pdf"),StandardOpenOption.READ);
 6
 7         // 2、分配指定大小的缓冲区
 8         ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
 9
10         // 3、读取本地文件,并写入发送channel
11         while (inChannel.read(byteBuffer)!=-1) {
12             byteBuffer.flip();// 切换到读模式
13             socketChannel.write(byteBuffer);
14             byteBuffer.clear();// 清空缓冲区
15         }
16
17         // 必须shutdown否则就没法切换到接收数据的模式
18         socketChannel.shutdownOutput();
19
20         System.out.println("client waiting reading server response");
21         // 接收服务端的数据
22         int length=0;
23         while((length=socketChannel.read(byteBuffer))!=-1){
24             byteBuffer.flip();
25             System.out.println(new String(byteBuffer.array(),0,length));
26             byteBuffer.clear();
27         }
28
29         System.out.println("end...");
30         inChannel.close();
31         socketChannel.close();
32     }
33
34     @Test
35     public void server() throws IOException{
36         // 1、获取通道
37         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
38         FileChannel outChannel=FileChannel.open(Paths.get("33.pdf"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
39
40         // 2、绑定连接
41         serverSocketChannel.bind(new InetSocketAddress(9898));
42         // 3、获取客户端的连接
43         SocketChannel accept = serverSocketChannel.accept();
44
45         // 4、分配指定大小的缓冲区
46         ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
47         // 5、接收客户端的数据,并保存到本地
48         while (accept.read(byteBuffer)!=-1) {
49             byteBuffer.flip();
50             outChannel.write(byteBuffer);
51             byteBuffer.clear();
52         }
53
54         System.out.println("server print ...");
55
56         byteBuffer.put("server success".getBytes());
57         byteBuffer.flip();//切换到读模式
58         accept.write(byteBuffer);
59
60         // 6、关闭连接
61         accept.close();
62         outChannel.close();
63         serverSocketChannel.close();
64     }

打印结果:

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示例:

  1 package com.expgiga.NIO;
  2
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.ServerSocketChannel;
  9 import java.nio.channels.SocketChannel;
 10 import java.util.Date;
 11 import java.util.Iterator;
 12 import java.util.Scanner;
 13
 30 public class TestNonBlockingNIO {
 31
 32     //客户端
 33     public void client() throws IOException {
 34
 35         //1.获取通道
 36         SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
 37
 38         //2.切换成非阻塞模式
 39         sChannel.configureBlocking(false);
 40
 41         //3.分配指定大小的缓冲区
 42         ByteBuffer buf = ByteBuffer.allocate(1024);
 43
 44         //4.发送数据给服务端
 45         Scanner scan = new Scanner(System.in);
 46
 47         while (scan.hasNext()) {
 48             String str = scan.next();
 49             buf.put((new Date().toString() + "\n" + str).getBytes());
 50             buf.flip();
 51             sChannel.write(buf);
 52             buf.clear();
 53         }
 54
 55         //5.关闭通道
 56         sChannel.close();
 57     }
 58
 59
 60     //服务端
 61     public void server() throws IOException {
 62         //1.获取通道
 63         ServerSocketChannel ssChannel = ServerSocketChannel.open();
 64
 65         //2.切换成非阻塞模式
 66         ssChannel.configureBlocking(false);
 67
 68         //3.绑定连接
 69         ssChannel.bind(new InetSocketAddress(9898));
 70
 71         //4.获取选择器Selector
 72         Selector selector = Selector.open();
 73
 74         //5.将通道注册到选择器上,并且指定“监听接受事件”
 75         ssChannel.register(selector, SelectionKey.OP_ACCEPT);
 76
 77         //6.轮询式获取选择器上已经准备就绪的事件
 78         while (selector.select() > 0) {
 79             //7.获取当前选择器中所有注册的选择键(已就绪的监听事件)
 80             Iterator<SelectionKey> it = selector.selectedKeys().iterator();
 81
 82             while (it.hasNext()) {
 83                 //8.获取准备就绪的事件
 84                 SelectionKey sk = it.next();
 85
 86                 //9.判断具体是什么事件准备就绪
 87                 if (sk.isAcceptable()) {
 88                     //10.若“接收就绪”,获取客户端连接
 89                     SocketChannel sChannel = ssChannel.accept();
 90
 91                     //11.切换成非阻塞的模式
 92                     sChannel.configureBlocking(false);
 93
 94                     //12.将该通道注册到选择器上
 95                     sChannel.register(selector, SelectionKey.OP_READ);
 96                 } else if (sk.isReadable()) {
 97                     //13.获取当前选择器上的"读就绪"状态的通道
 98                     SocketChannel sChannel = (SocketChannel) sk.channel();
 99
100                     //14.读取数据
101                     ByteBuffer buf = ByteBuffer.allocate(1024);
102
103                     int len = 0;
104                     while ((len = sChannel.read(buf)) > 0) {
105                         buf.flip();
106                         System.out.println(new String(buf.array(), 0, len));
107                         buf.clear();
108                     }
109                 }
110
111                 //15.取消选择键SelectionKey
112                 it.remove();
113             }
114         }
115     }
116
117 }

原文地址:https://www.cnblogs.com/shamo89/p/9612951.html

时间: 2024-11-10 14:32:07

NIO之阻塞IO与非阻塞IO的相关文章

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

最近在研究java IO.NIO.NIO2(或者称AIO)相关的东西,有些概念还是要明确下. 按照<Unix网络编程>的划分,IO模型可以分为:阻塞IO.非阻塞IO.IO复用.信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO. 如何区分呢?首先一个IO操作其实分成了两个步骤: 1.发起IO请求 2.实际的IO操作 阻塞和非阻塞IO:在于第一个步骤是否会会被阻塞,如果会则是阻塞IO,否则是非阻塞IO. 异步和非异步(同步)IO:在于第二个步骤是否会阻塞,如果实际的I

Java阻塞IO与非阻塞IO

IO: IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 阻塞与非阻塞: 一辆从 A 开往 B 的公共汽车上,路上有很多点可能会有人下车.司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好? 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车. ( 类似阻塞式 ) 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车. ( 类似非阻

阻塞IO、非阻塞IO的区别

1.类与类之间的关系:依赖,实现,泛化(继承),关联,组合,聚合. 1)依赖(虚线):一个类是 另一个类的函数参数 或者 函数返回值. 2)实现(实线加小圆):对纯虚函数类(抽象类)的实现. 3)继承 即 泛化(实线加空心三角形,从子类指向父类):表示一个类与另一个类之间的继承关系: 4)关联(实线):一个类是另一个类的成员变量. 5)聚合(菱形加实线):整体和部分的关系.不太紧密的(如:汽车与发动机,汽车可以选择各种型号的发动机) 6)组合(实心菱形加实线):生命体整体与部分的关系.紧密的关系

python(十)下:事件驱动与 阻塞IO、非阻塞IO、IO多路复用、异步IO

上节的问题: 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 一.事件驱动模型介绍 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求 第三种就是协程.时间驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 论事件驱动模型 在UI编程中,,常常要对鼠标点击进行相应,

同步IO,异步IO,阻塞IO,非阻塞IO的联系与区别

转载 POSIX 同步IO.异步IO.阻塞IO.非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下. POSIX(可移植操作系统接口)把同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO 按POSIX的描述似乎把同步和阻塞划等号,异步和非阻塞划等号,但是为什么有的人说同步IO不等于阻塞IO呢?先来说说几种常见的IO模型吧. IO模型 这里统一使用Linux下的系统调用recv

并发编程 - IO模型 - 1.io模型/2.阻塞io/3.非阻塞io/4.多路复用io

1.io模型提交任务得方式: 同步:提交完任务,等结果,执行下一个任务 异步:提交完,接着执行,异步 + 回调 异步不等结果,提交完任务,任务执行完后,会自动触发回调函数同步不等于阻塞: 阻塞:遇到io,自己不处理,os会抢走cpu ,解决办法:监测到io,gevent切换到其他任务,类似欺骗os 非阻塞:cpu 运行 IO分类: 1.阻塞IO blocking IO 2.非阻塞IO nonblocking IO 3.IO多路复用 IO multiplexing 4.信号驱动IO signal

什么是阻塞式和非阻塞io流?

阻塞IO:socket 的阻塞模式意味着必须要做完IO 操作(包括错误)才会返回. 非阻塞IO:非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功. 两者区别: 所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止. 对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待 . 一种常用做法是:每建立一个Socket连接时,同时创建一个新线程对

阻塞IO、非阻塞IO、IO多路复用、同步IO、异步IO 的理论

同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西.这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同.所以,为了更好的回答这个问题,我先限定一下本文的上下文.本文讨论的背景是Linux环境下的ne

理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO

完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念:- 用户空间和内核空间- 进程切换- 进程的阻塞- 文件描述符- 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,

网络编程之IO模型——非阻塞IO

网络编程之IO模型--非阻塞IO 非阻塞IO(non-blocking IO) Linux下,可以通过设置socket使其变为non-blocking.当对一个non-blocking socket执行读操作时,流程是这个样子: 从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error.从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果.用户进程判断结果是一个error时,它就