分享

同步与异步/阻塞与非阻塞/回调【面试+工作】

 Java帮帮 2020-01-02


一、同步与异步

概念性

  同步异步通常用来形容一次方法调用。

  • 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

  • 异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

图示例

举个例子

  打个比方,比如我们去购物,如果你去商场实体店买一台空调,当你到了商场看中了一款空调,你就想售货员下单。售货员去仓库帮你调配物品。这天你热的实在不行了。就催着商家赶紧给你配送,于是你就等在商场里,候着他们,直到商家把你和空调一起送回家,一次愉快的购物就结束了。这就是同步调用。

  不过,如果我们赶时髦,就坐再家里打开电脑,在网上订购了一台空调。当你完成网上支付的时候,对你来说购物过程已经结束了。虽然空调还没有送到家,但是你的任务都已经完成了。商家接到你的订单后,就会加紧安排送货,当然这一切已经跟你无关了,你已经支付完成,想什么就能去干什么了,出去溜达几圈都不成问题。等送货上门的时候,接到商家电话,回家一趟签收即可。这就是异步调用。

二、阻塞与非阻塞

阻塞与非阻塞关注的是交互双方是否可以弹性工作。假设对象 A 和对象 B 进行交互,而对象 B 对一个问题需要思考一段时间才能回复 A,那么对象 A 可以选择等待对象 B 回复,这种方式就是一种阻塞式交互,与此同时,对象 A 可以选择在对象 B 进行思考的时间去完成别的工作,等到对象 B 完成思考后再进行后续交互,这种方式就是一种非阻塞式的交互。

一般来说,阻塞与非阻塞式用来形容 CPU 消耗的。我们把 CPU 停下来等待慢操作完成以后再接着工作称为阻塞;把 CPU 在慢操作完成之前去完成其他工作,等慢操作完成后再接着工作称为非阻塞。

而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。 

一般来说IO模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。

同步阻塞IO 

在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。

同步非阻塞IO 

在此种方式下,用户进程发起一个IO操作以后可返回做其它事情, 

但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。

异步阻塞IO 

此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成, 

等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别同步必须等待或者主动的去询问IO是否完成,

异步非阻塞IO 

在此种模式下,用户进程只需要发起一个IO操作然后立即返回, 

等IO操作真正的完成以后,应用程序会得到IO操作完成的通知, 

此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作因为真正的IO读取或者写入操作已经由内核完成了。

三、回调

回调:简单来说,就是我调用你的函数,你调用我的函数。正规一点的说法就是类A的a()函数调用类B的b()函数,当类B的b()函数的执行时又去调用类A里的函数。是一种双向的调用方式。一般情况下,回调分两种,分别是同步回调和异步回调。

同步回调:一种双向调用模式,被调用方在函数被调用时也会调用对方的函数。下面用一个计算器的例子来展示(为了方便,写在一个文件里)。

运行结果: 
10452 + 423 = 10875 
/========================/

异步回调:一种类似消息或事件的机制,被调用方在函数在收到某种讯息或发生某种事件时,才去调用对方的函数,即通过异步消息进行通知。简单来说,类A的a()函数调用类B的b()函数,但是b()函数很耗时,不确定什么时候执行完毕,如果是同步调用的话会等b()执行完成后才往下执行回调类A中的函数,如果是异步回调的话调用了b()函数,虽然b()函数没有执行完,但仍然继续往下执行,为了完成这点,就需要另开一个线程了。下面还是用一个计算器的例子来展示(为了方便,写在一个文件里)。

运行结果: 

/========================/ 

10452 + 423 = 10875

你会发现,输出”/====/”明明是放在代码的最后执行的,结果却先执行输出了,这是因为开了另一个线程,而异步回调和同步回调最大的不同就是异步回调里新建了一个子线程。异步回调常见于请求服务器数据,当取到数据时,会进行回调。

扩展知识:另一种回调(同步),主要是为解决当实现的逻辑不明确时的问题。还是用计算器的例子,比如不一定用计算器进行加法运算,也有可能进行乘法运算(为了方便,写在一个文件里)。

运行结果: 
10452 * 423 = 4421196 
/========================/

异步回调也同理,在Calculator类的calculation()函数里新建一个线程就行了,这里就不举例了。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多