同步与异步

同步

进程和线程都是并发执行的,不同的进程之间,存在不同的相互制约关系。为了协调这种制约关系,引入了同步的概念。

举例说明:

系统计算1+2×3。假设有两个进程进行计算该公式,一个是加法进程,一个是乘法进程。如果要计算正确,要先执行乘法进程,然后执行加法进程。但是因为系统的异步性(并发、虚拟、异步、共享),若是不加以制约,无法保证乘法进程在加法进程之前执行的。所以要制定一些机制去约束加法进程,让他在乘法进程完成之后运行。

同步

同步也称为直接制约关系,是指一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。

互斥

互斥也称为间接制约关系,当个一个进程进入临界区使用临界资源的时候,另一个进程必须等待,当占用临界资源的进程退出临界区之后,另一个进程才允许访问此临界资源

同时为了禁止两个进程同时进入临界区,同步机制因遵循一下的原则:

临界区:进程访问临界资源的代码,它同一时刻只能由一个进程执行,如java中的synchronized关键字在方法上,那临界区就是整个方法内部。而如果是使用synchronized代码块,那临界区就指的是代码块内部的区域。

  1. 空闲让进:临界区空闲时,可以允许一个请求进入临界区的进程进入临界区
  2. 忙则等待:当已有进程进入临界区时,其他试图进入临界区的进程必须等待
  3. 有限等待:对已经请求进入临界区的进程,应保证其在有限的时间进入临界区
  4. 让权等待:当进程不能进入临界区时,应立即释放处理器,防止进程忙等。

经典同步问题

生产者-消费者问题、读写者问题、哲学家就餐问题

异步

异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

举例说明:

发短信,A发短信给B,发送给B即可,发送完之后,可以做其他的事情,短信来了,手机会提醒

阻塞与非阻塞

阻塞和非阻塞是从CPU的消耗而言的。

阻塞:就是CPU停下来等待一个慢操作完成之后CPU再继续执行其他的事情

非阻塞: 在慢操作执行的时候,CPU会去执行其他的事情,等这个慢操作完成之后,CPU继续完成其后续事情

虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU使用时间能不能补偿系统的切换成本需要好好评估。

以上两种概念的花式组合

这里主要是和I/O相关

排列组合之后,共四种情况:同步阻塞同步非阻塞异步阻塞异步非阻塞

组合方式 性能
同步阻塞 最常用的一种用法,使用也是最简单的,但是 I/O 性能一般很差,CPU 大部分在空闲状态。(BIO)
同步非阻塞 提升 I/O 性能的常用手段,就是将 I/O 的阻塞改成非阻塞方式,尤其在网络 I/O 是长连接,同时传输数据也不是很多的情况下,提升性能非常有效。这种方式通常能提升 I/O 性能,但是会增加 CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗,也就是系统的瓶颈是在 I/O 还是在 CPU 上。(NIO)
异步阻塞 这种方式在分布式数据库中经常用到,例如在网一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其它机器上,这些备份记录通常都是采用异步阻塞的方式写 I/O。异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况。
异步非阻塞 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之间的消息同步机制一般用这种 I/O 组合方式。如 Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。它适合同时要传多份相同的数据到集群中不同的机器,同时数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高。(AIO)

虽然异步非阻塞能够提升 I/O 的性能,但是也会带来一些额外的性能成本,例如会增加线程数量从而增加 CPU 的消耗,同时也会导致程序设计的复杂度上升。如果设计的不合理的话反而会导致性能下降。在实际设计时要根据应用场景综合评估一下。