阻塞非阻塞和同步异步的区分 参考一些书籍

编程中一直对这两个概念不是很理解,在网上搜了很多资料大概描述的其实都很模糊,有时候还自相矛盾,很容易搞混,这里说一下我对这两个概念的理解。
首先看一下相关技术书籍对这两个概念的描述,下面分别是摘自《深入理解Java核心技术》和《Java并发程序设计中的》的内容。
摘自《深入理解Java核心技术》14.2:

当I/O操作发生时,一定是有两方参与的,分别是调用方和被调用方。阻塞和非阻塞描述的是调用方,同步和异步描述的是被调用方。
例如A调用B:
1.如果是阻塞,那么A在发出调用命令后,要一直等待B返回结果。
2.如果是非阻塞,那么A在发出调用命令后,不需要等待,可以去做自己的事情。
3.如果是同步,那么B在收到A的调用命令后,会立即执行要做的事,A的本次调用可以得到结果
4.如果是异步,那么B在收到A的调用命令后,不保证会立即执行要做的事,但是保证会做,B在做好了之后会通知A。A的本次调用得不到·结果,但是B执行完成要做的事之后会通知A。
因为同步/异步与阻塞/非阻塞描述的对象不同,所以这二者之间是没有必然联系的。也就是说,同步不一定阻塞,异步也不一定非阻塞。
只不过通常很少存在异步且阻塞的场景,所以很多人误以为同步一定是非阻塞的、异步一点事非阻塞的。

摘自《Java高并发程序设计》1.2:

同步和异步通常用来形容一次方法的调用。同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而异步方法通常会在另外一个线程中“真实”地执行。整个过程,不会阻碍调用者的工作。对于调用者来说,异步调用似乎是一瞬间就完成的。如果异步调用需要返回结果,那么当这个异步调用真实完成时,则会通知调用者。
阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待。等待会导致线程挂起,这种情况就是阻塞。此时,如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞在这个临界区上的线程都不能工作。 非阻塞的意思与之相反,它强调没有一个线程可以妨碍其他线程执行。所有的线程都会尝试不断前向执行。

两本书籍的描述切入点不一样,乍一看两个书籍描述的可能还有点冲突,反而让人更迷糊。
在我看来阻塞和非阻塞必然是线程或进程相关的,阻塞指的是如果线程无法立即获取到资源是是否需要暂停线程等待资源准备完成,非阻塞则与之相反,即我不会等待我先去干别的事情,等到资源准备好了再通知线程进行处理。
同步即所有的代码都是按顺序执行的,可以理解为这个事情不干完了我就不干别的事情。异步则不可预测顺序的,异步调用必须伴随着通知,可以理解为等到取值完成后我再进行后续的操作,我先去干别的事情。

同步异步和阻塞非阻塞搭配就会出现如下四种组合:

  1. 同步阻塞,如果不能立即获取数据,则线程阻塞等待数据返回。因为程序阻塞了,需要有其他程序将程序唤醒(这里很重要),如java类FileInputStream的readBytes操作。
  2. 同步非阻塞,如果不能立即获取数据,则循环检查数据是否完成。这里不需要其他线程参与唤醒操作。比如java类AtomicInteger的自增操作,如果不能加1就循环执行。
  3. 异步非阻塞,如果不能立即获取数据,则立即返回执行其他操作。等到数据返回了,接收到通知后再进行后续的处理工作(这里后续的处理操作是别的线程去做了,因为当前线程可能已经退出了)。
  4. 异步阻塞,这应该是一个并不存在的组合,异步和阻塞是互斥的。既然都可以异步执行,你还阻塞干什么呢,执行完成之后再通知你不就好了吗,异步的目的不就是无法立即获取数据让线程去干别的事情吗,如果没有别的事情可做就同步阻塞等待就可以了

总结:
如果一个程序说他是异步的,那他肯定非阻塞的,因为并不存在异步阻塞。开启异步的目的就是不想让当前线程阻塞,让他去干其他事情。就是你需要获取一个资源或发出一个通知,但是你并不能立即获取这个资源或者知道通知是否发送成功,这个时候你就需要线程去干别的事(这里的别的事指不依赖前面调用返回结果的代码)。等结果出来了再对结果进行处理,如对资源解析或者重新发送消息。

posted @ 2022-07-03 19:39  大兴神  阅读(414)  评论(4编辑  收藏  举报