同步(synchronous)/异步(asynchronous),阻塞(blocking)/非阻塞(non-blocking)两组概念在不同场合有不同的含义。
在操作系统中
阻塞状态是指正在执行的进程由于发生某事情而暂时无法继续执行时,便放弃处理机而处于暂停状态。
进程具有异步性,指:进程各自独立,不可预知的速度向前推进,或者说进程实体按异步的方式运行。正式由于有异步性,所以要同步。
进程同步:对多个相关进程在执行次序上进行协调,以使并发执行的进程之间能有效共享资源和互相合作,从而使程序的执行具有可再现性。
这里讲的同步异步需在在多个进程之间,是讲进程推进的方式。异步就是进程之间没有关系,互不干扰。同步就是进程有关系,有干扰关系的。
在通信技术上
这里的同步异步,是讲调用方是否感知到被调用方调用成功了并调用方能否得到反馈。
比如,小明他妈让小明去车站接人,小明到了车站一看客人还没到,不返回家里告诉他妈,而是一直等到客人到达之后才返回告诉他妈。这就是同步的通信。
比如,还是小明他妈让小明去车站接人,但是客人到达的时间并不确定,然后每隔十分钟小明都会再去车站看一遍客人到没到,立即返回家里告诉他妈。这就是异步的通信。
在IO操作上
阻塞和非阻塞是指当进程访问的数据如果尚未就绪,进程是否需要等待。
比如,小明他妈让小明去车站接人,在小明把客人接到家之前,小明他妈什么都不干,在家里傻等。这就是阻塞的IO模型。
比如,还是小明他妈让小明去车站接人,在小明把客人接到家之前,小明他妈可以在家里洗菜、做饭、打扫卫生,直到客人到达,再停下手中的或去迎接客人。这就是非阻塞的IO模型。
同步和异步是指访问数据的机制,同步一般指主动请求并等待I/O操作完毕的方式,当数据就绪后在读写的时候必须阻塞(区别就绪与读写两个阶段,同步的读写必须阻塞)。异步则指主动请求数据后便可以继续处理其它任务,随后等待I/O操作完毕的通知,这可以使进程在数据读写时也不阻塞,但是会等待“通知”。
同步大多是阻塞的,但是也有非阻塞。因为线程可以干别的事情,不一定一直等待,可以轮询。
异步大多为非阻塞的,但是也可以阻塞。因为线程可以不干别的事情,一直在等待通知。
同步与异步、阻塞与非阻塞之间有很多相似的地方,在一些资料中两者等同,是可以互换的。
java中的NIO
java 6版本开始引入的NIO(即 new IO)包,通过Selecters提供了非阻塞式的IO。
Java NIO的通道类似流,但又有些不同:
(1)既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
(2)通道可以异步地读写。
(3)通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。
Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。
阻塞与非阻塞IO
Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。
选择器(Selectors)
Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
Selector允许单线程处理多个Channel。如果应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
下面是在一个单线程中使用一个Selector处理3个Channel的图示:
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
在通道上可以注册我们感兴趣的事件。一共有以下四种事件:
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT
客户端连接服务端事件 SelectionKey.OP_CONNECT
读事件 SelectionKey.OP_READ
写事件 SelectionKey.OP_WRITE
Java NIO相对于旧的java.io库来说,并不是要取代,而是提出的三个新的设计思路:
(1)对原始类型的读/写缓冲的封装
(2)基于Channel的读写机制,对Stream的进一步抽象。
(3)事件轮询/反应设计模式(即Selector机制)
按上述思路,而Channel机制是作为Stream的进一步抽象而产生的,那么Channel和Stream相比有什么不同呢?按字面理解实际上就可以获得信息:Stream作为流是有方向的,而Channel则只是通道,并没有指明方向。因此,读写操作都可以在同一个Channel里实现。Channel的命名强调了nio中数据输入输出对象的通用性,为非阻塞的实现提供基础。
在Channel的实现里,也存在只读通道和只写通道,这两种通道实际上抽象了Channel的读写行为。
至于Channel的IO阻塞状态读写,则和传统的java.io包类似。但多了一层缓冲而已。因此,按照原来的设计思路来用nio也是可行的,不过nio的设计本质上我觉得还是非阻塞输入输出控制,把控制权重新交给程序员。
因此,java.nio从设计角度看,就不是替代java.io包,而是为java.io提供更多的控制选择。
Channel和Buffer的关系
Buffer的作用主要体现在Channel的非阻塞状态下。Channel如果不是阻塞的,那么每次调用必须立即返回,那样,读写操作都不至于影响对方。那么,立即返回意味着能够读写多少数据呢?这是不确定的,依赖于当前的传输状况。因此,作为缓冲的Buffer就为信息的完整性提供一个保障。每次读写操作都先把数据放到Buffer里面,然后多次调用Channel的读写方法对数据进行操作,依靠对Buffer的状态来判断数据的完整性。
以下是Java NIO里关键的Buffer实现:
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
这些Buffer覆盖了能通过IO发送的基本数据类型:byte,short, int, long, float, double 和 char。
Java NIO 还有个Mappedyteuffer,用于表示内存映射文件。
正如上面所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道。
上面的图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的内容写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。
非阻塞式IO实例
java NIO中的重要实现类:
FileChannel 从文件中读写数据。
DatagramChannel 能通过UDP读写网络中的数据。
SocketChannel 能通过TCP读写网络中的数据。
ServerSocketChannel 可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
下面利用SocketChannel与ServerSocketChannel来实现非阻塞式的网络通信。
服务端:
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.Iterator; public class NIOServer { //通道管理器 private Selector selector; /** * 获得一个ServerSocket通道,并对该通道做一些初始化的工作 *@param port 绑定的端口号 *@throws IOException */ public void initServer(int port) throws IOException { // 获得一个ServerSocket通道 ServerSocketChannel serverChannel =ServerSocketChannel.open(); // 设置通道为非阻塞 serverChannel.configureBlocking(false); // 将该通道对应的ServerSocket绑定到port端口 serverChannel.socket().bind(newInetSocketAddress(port)); // 获得一个通道管理器 this.selector = Selector.open(); //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 */ public void listen() throws IOException { System.out.println("服务端启动成功!"); // 轮询访问selector while (true) { //当注册的事件到达时,方法返回;否则,该方法会一直阻塞 selector.select(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 Iterator ite =this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key =(SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); // 客户端请求连接事件 if(key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key .channel(); // 获得和客户端连接的通道 SocketChannelchannel = server.accept(); // 设置成非阻塞 channel.configureBlocking(false); //在这里可以给客户端发送信息 channel.write(ByteBuffer.wrap(newString("向客户端发送了一条信息").getBytes())); //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 channel.register(this.selector,SelectionKey.OP_READ); // 获得了可读的事件 } else if(key.isReadable()) { read(key); } } } } /** * 处理读取客户端发来的信息 的事件 *@param key *@throws IOException */ public void read(SelectionKey key) throws IOException{ // 服务器可读取消息:得到事件发生的Socket通道 SocketChannel channel =(SocketChannel) key.channel(); // 创建读取的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = newString(data).trim(); System.out.println("服务端收到信息:"+msg); ByteBuffer outBuffer =ByteBuffer.wrap(msg.getBytes()); channel.write(outBuffer);// 将消息回送给客户端 } /** * 启动服务端测试 *@throws IOException */ public static void main(String[] args)throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }客户端:
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.SocketChannel; import java.util.Iterator; public class NIOClient { //通道管理器 private Selector selector; /** * 获得一个Socket通道,并对该通道做一些初始化的工作 *@param ip 连接的服务器的ip *@param port 连接的服务器的端口号 *@throws IOException */ public void initClient(String ip,int port) throws IOException { // 获得一个Socket通道 SocketChannel channel =SocketChannel.open(); // 设置通道为非阻塞 channel.configureBlocking(false); // 获得一个通道管理器 this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 //用channel.finishConnect();才能完成连接 channel.connect(newInetSocketAddress(ip,port)); //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 channel.register(selector,SelectionKey.OP_CONNECT); } /** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 *@throws IOException */ public void listen() throws IOException { // 轮询访问selector while (true) { selector.select(); // 获得selector中选中的项的迭代器 Iterator ite =this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key =(SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); // 连接事件发生 if (key.isConnectable()){ SocketChannelchannel = (SocketChannel) key .channel(); // 如果正在连接,则完成连接 if(channel.isConnectionPending()){ channel.finishConnect(); } // 设置成非阻塞 channel.configureBlocking(false); //在这里可以给服务端发送信息 channel.write(ByteBuffer.wrap(newString("向服务端发送了一条信息").getBytes())); //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 channel.register(this.selector,SelectionKey.OP_READ); // 获得了可读的事件 } else if(key.isReadable()) { read(key); } } } } /** * 处理读取服务端发来的信息 的事件 *@param key *@throws IOException */ public void read(SelectionKey key) throwsIOException{ //和服务端的read方法一样 } /** * 启动客户端测试 *@throws IOException */ public static void main(String[] args)throws IOException { NIOClient client = new NIOClient(); client.initClient("localhost",8000); client.listen(); } }
同步异步与阻塞非阻塞
时间: 2024-10-14 11:38:08
同步异步与阻塞非阻塞的相关文章
理解同步异步与阻塞非阻塞
本篇文章我准本从三个大方面来解释下同步异步.阻塞非阻塞的知识,第一个方面主要是说下,到底什么是同步异步.阻塞非阻塞:第二个方面主要是解释下在I/O场景下,同步异步阻塞非阻塞又是怎么定义的,第三个方面介绍下在unix下同步异步又有哪些阻塞非阻塞IO. 1.同步异步与阻塞非阻塞 首先从大的方面来说,"阻塞"与"非阻塞"与"同步"与"异步"不能简单的从字面理解,提供一个从分布式系统角度的回答. 1).同步与异步 同步和异步关注的是消
同步/异步与阻塞/非阻塞
一.同步与异步同步/异步, 它们是消息的通知机制 1. 概念解释A. 同步所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回. 按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等).但是一般而言,我们在说同步.异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务.最常见的例子就是 SendMessage.该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回.当对方处理完毕以后,该函数才把消息处理函数所返回的值返回给调用者. B.
讲一讲什么叫阻塞非阻塞同步异步
1.讲一讲什么叫阻塞非阻塞同步异步全是用来形容方法的,形容一个方法返回值状态的. 2.io读取,网络读取,jdbc读取,这些流的操作都是bio的,都是阻塞的. 3.所以沃恩一般在处理io操作时,都采用多线程来提高bio的效率. 4.io操作,就是本地文件,网络,数据嘛嘛.所以在这三种读取数据时,都要采用多线程提高效率. 5.多线程处理阻塞方法时,只不过是避免了主线程的阻塞,但是让子线程,也就是处理每个http request的线程去发生阻塞了. 6.传统的古老的开发方式: 单线程执行阻塞方法->
基础入门_Python-网络编程.分分钟掌握阻塞/非阻塞/同步/异步IO模型?
概念梳理: 故事独白: 满满爱喝茶,废话不多说,开始煮开水. 出场人物: 满满, 普通水壶, 高级水壶(水开会响) 1. 满满把水壶放在火上, 站在那里等水开(同步阻塞) 满满觉得自己有点儿傻逼~ 2. 满满把水壶放在火上,去客厅看电视,时不时的去厨房瞅瞅水开木有(同步非阻塞) 满满觉得自己还是有点傻~,于是买了个高级水壶, 水开后会响~ 3. 满满把高级水壶放在火上, 站在那里等水开(异步阻塞) 满满想高级水壶水开会自己叫~为毛不去看个电视哪? 4. 满满把高级水壶放在火上, 去客厅看电视,
同步/异步-阻塞/非阻塞
(A)同步和异步,是针对 调用结果是如何返回给调用者来说的,即调用的结果是调用者主动去获取的(比如一直等待recvfrom或者设置超时等待select),则为同步,而调用结果是被调用者在完成之后通知调用者的,则为异步(比如windows的IOCP).(B)阻塞和非阻塞,是针对调用者所在线程是否在调用之后主动挂起来说的,即如果在线程中调用者发出调用之后,再被调用这返回之前,该线程主动挂起,则为阻塞,若线程不主动挂起,而继续向下执行,则为非阻塞. 这样,在网络IO中,同步异步,阻塞非阻塞,就可以形成
深入理解同步/异步与阻塞/非阻塞区别 (转)
转载自:http://chuansong.me/n/2124760 几年前曾写过一篇描写同步/异步以及阻塞/非阻塞的文章,最近再回头看,还存在一些理解和认知误区,于是重新整理一下相关的概念,希望对网络编程的同行能有所启发. 同步与异步 首先来解释同步和异步的概念,这两个概念与消息的通知机制有关. 举个例子,比如一个用户去银行办理业务,他可以自己去排队办理,也可以叫人代办,办完之后再告知用户结果.对于要办理这个银行业务的人而言,自己去办理是同步方式,而别人代办完毕再告知则是异步方式. 两者的区别在
异步/同步、阻塞/非阻塞的理解
[同步和异步] 通俗的讲:同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式. 异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式.同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕 概念从现实中来:同步:打电话,你拨通电话后必须等在那里,直到对方有人接了,你们才能通信或者说你才能继续打下一个电话.异步:发短信,你可以发
(转)同步异步/阻塞非阻塞 和 5种linux网络通信模型
会阻塞的函数:connect, accept,send/recv/sendto/recvfrom等读写函数. 不会阻塞的函数:bind, listen,socket, closesocket. linux网络通信模型有: 阻塞IO模型(同步),非阻塞IO模型(拷贝同步),IO复用模型(多线程同步),信号驱动IO模型((拷贝同步),异步IO模型(异步). node.js对同步/异步,阻塞非阻塞的解释: 线程在执行中如果遇到磁盘读写或网络通信(统称为I/O 操作),通常要耗费较长的时间,这时 操作系
Java 中阻塞非阻塞io以及同步异步IO
然后借鉴下<Unix网络编程卷>中的理论: IO操作中涉及的2个主要对象为程序进程.系统内核.以读操作为例,当一个IO读操作发生时,通常经历两个步骤: 1,等待数据准备 2,将数据从系统内核拷贝到操作进程中 例如,在socket上的读操作,步骤1会等到网络数据包到达,到达后会拷贝到系统内核的缓冲区:步骤2会将数据包从内核缓冲区拷贝到程序进程的缓冲区中. 阻塞(blocking)与非阻塞(non-blocking)IO IO的阻塞.非阻塞主要表现在一个IO操作过程中,如果有些操作很慢,比如读操作
Socket 同步/异步 与阻塞/非阻塞区别
在网上看了很多答案,也没找到合适的,也许本文也不是合适答案:) 同步和异步关注的是消息通信机制,而阻塞非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态. 同步/异步是API 被调用者的通知方式.阻塞/非阻塞则是API 调用者的等待方式. 同步.异步和阻塞.非阻塞是组合关系. 因此有4种方式: 同步阻塞 同步非阻塞 异步阻塞 异步非阻塞 ------------------------------------------------------------------------ recv