http://www.iteye.com/topic/1131578#2399581
http://www.ibm.com/developerworks/cn/linux/l-async/
https://www.ibm.com/developerworks/cn/java/j-lo-javaio/
同步和异步站在任务调度者看任务之间有无顺序关系;
阻塞和非阻塞是站在CPU角度看内设(cpu和内存)和外设之间的速度匹配上差距产生的认识。
同步与异步
所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。而异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。我们可以用打电话(同步)和发短信(异步)来很好的比喻同步与异步操作。
在设计到 IO 处理时通常都会遇到一个是同步还是异步的处理方式的选择问题。因为同步与异步的 I/O 处理方式对调用者的影响很大,在数据库产品中都会遇到这个问题。因为 I/O 操作通常是一个非常耗时的操作,在一个任务序列中 I/O 通常都是性能瓶颈。但是同步与异步的处理方式对程序的可靠性影响非常大,同步能够保证程序的可靠性,而异步可以提升程序的性能,必须在可靠性和性能之间做个平衡,没有完美的解决办法。
阻塞与非阻塞
阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在这个慢的操作在执行时 CPU 去干其它别的事,等这个慢的操作完成时,CPU 再接着完成后续的操作。虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估。
我的理解:
引用
打个比喻吧:司机开车在路上阻车了,如果采用阻塞模式,则司机停下来什么事也不干,就眼巴巴地直等前面的车启动,他继续跟车。如果采用非阻塞模式,则前面阻车后,司机停下来看报纸,听音乐,等前面车走动后,他再放下报纸,继续跟车。
显然,非阻塞式司机的时间利用率提高了,而阻塞式司机时间白白浪费了,前者往往是乐天派,而后者是抱怨派。
但是个人总觉得,这两段话还是没有把“同步与异步”与“阻塞与非阻塞”的概念说清楚,我有以下几点疑问:
1)同步与异步 和 阻塞与非阻塞 所描述的主体是什么,是否前者描述是主体是调用序列(或说两个任务),而后者描述主体是CPU的调度呢?
2)同步与异步和阻塞与非阻塞的真正区别是什么,总觉得同步是对应阻塞,而异步是对应非阻塞的。
解释:
同步与异步是站在任务调用者角度而言的。
任务调用者调用的操作跟任务调用者自身的操作不是顺序(同步)的操作,这个调用就是异步的操作。异步操作背后的实现往往采用多线程(看语言对任务的封装粒度)。
用Java代码表示就是:
A:
Java代码
被调用者
1. public class A extends Thread
2. {
3. public void run()
4. {
5. //各种操作
6. }
7.
8. public void call()
9. {
10. start();
11. }
12. }
B:
Java代码
调用者
1. public class B
2. {
3. public void operation()
4. {
5. //各种操作
6. }
7.
8. public void call()
9. {
10. new A().call(); //与调用者B后续的操作不是顺序执行的
11. operation();
12. }
13. }
阻塞与非阻塞是站在CPU角度来看的。计算机各个部件的运行速度是不一样的,其中CPU速度最快。早期CPU与内存速度是一致的,访问内存并不需要额外等待,这里把CPU与内存视为一个整体作为计算机的内部系统——内设。而其它设备——外设的运行速度就很慢了。
当计算机要做的下一个事情不是仅在内设中就能直接处理的,需要外设的参与时,CPU会将事情交给外设去做。如果内设等待慢腾腾的外设处理完成并返回结果后才接着做后边的事情,这种模式就是阻塞模式;反之CPU将事情交给外设之后,内设就直接去做后边的事情不等待,这种模式就是非阻塞模式。所以阻塞与非阻塞发生的场景就是内设与外设交互之时。
内设与外设的交互往往被抽象为IO,所以阻塞与非阻塞几乎总是伴随IO出现(但这不是必须的)。
由此可见,同步/异步与阻塞/非阻塞是两组无关的概念。
同步阻塞很常见,就不再列举了。
异步阻塞的Java代码表示:基于上边的代码,仅改动A中run的代码。
Java代码
1. public void run()
2. {
3. try{
4. ServerSocketChannel ssc = ServerSocketChannel.open();
5. ServerSocket socket = ssc.socket();
6. InetSocketAddress add = new InetSocketAddress(11111);
7. socket.bind(add,2);
8. ssc.configureBlocking(true); //使用阻塞模式,默认行为
9. while(true)
10. {
11. SocketChannel accept = ssc.accept(); //这里会阻塞
12. if(accept!=null)
13. {
14. System.out.println(accept);
15. }else
16. {
17. System.out.println("hehe");
18. Thread.sleep(500);
19. }
20. }
21. }catch(Exception ex){}
22. }
那么B的call()执行时,会明显的发生异步阻塞。
异步非阻塞的Java代码表示(如果A不是多线程程序,就成为了同步非阻塞):
Java代码
1. public void run()
2. {
3. try{
4. ServerSocketChannel ssc = ServerSocketChannel.open();
5. ServerSocket socket = ssc.socket();
6. InetSocketAddress add = new InetSocketAddress(11111);
7. socket.bind(add,2);
8. ssc.configureBlocking(false); //使用非阻塞模式
9. while(true)
10. {
11. SocketChannel accept = ssc.accept(); //这里不会阻塞
12. if(accept!=null)
13. {
14. System.out.println(accept);
15. }else //执行其它的事情
16. {
17. System.out.println("hehe"); //会看到这一步
18. Thread.sleep(500);
19. }
20. }
21. }catch(Exception ex){}
22. }
然后你分别使用telnet 127.0.0.1 11111,测试看看不同行为。
---------
补充一下,上述的回答是按LZ提供的上下文考虑的。
在阻塞/非阻塞中,如果把CPU视为任务调用者,那么非阻塞中外设相对内设是异步的。而同步/异步中,异步操作在调用者看来也总是不会阻塞的,但站在执行者或CPU的角度来看这一个操作就不一定不会阻塞了。
我的小结是:
1.同步/异步针对任务调度来说的,其实现机制一般是多线程,异步情况即A任务调用B任务时,A任务在A线程上执行,而B任务在B线程上执行;
2.阻塞/非阻塞一般是针对IO来说的,快慢双方在交互时,如果快的一方停下来啥事不干专等慢的一方完成(苦逼型的从一而终 ),即阻塞,快的一方在慢的一方未完成时,干其它的事,慢的一方完成后,再回过头来处理慢的一方的数据,即为非阻塞(快乐型的脚踏多船 )
同步/异步的实现机制是多线程,由于阻塞/非阻塞存在快的一方同步处理多个慢的一方,感觉其内部实现机制应该也是多线程吧(类似于一个扫描监控线程在快和慢的双方进行通知),谁能说说阻塞/非阻塞的内部真正的实现机制呢? 阻塞就是等待被依赖任务完成,非阻塞基于观察者模式;只需要依赖者观察被依赖者任务完成情况,当被依赖者完成了任务后发送通知给依赖者就可以。