怎样理解阻塞非阻塞与同步异步的区别?

  • 发现很多人对这两个概念往往混为一谈(包括本人,不是很理解)。
  • 阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。

一  定义理解方式

 1.同步与异步

   同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication),所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由*调用者*主动等待这个*调用*的结果

  而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。典型的异步编程模型比如Node.js

  
  举个通俗的例子:
  你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
  而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。


2. 阻塞与非阻塞

  阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。

    阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

 

  还是上面的例子
  你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
  在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
 

二 老张煮开水(经典理解)

   老张爱喝茶,废话不说,煮开水。出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。

  1 老张把水壶放到火上,立等水开。(同步阻塞)
    老张觉得自己有点傻
  2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
    老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
  3 老张把响水壶放到火上,立等水开。(异步阻塞)
    老张觉得这样傻等意义不大
  4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
    老张觉得自己聪明了。
 
  所谓同步异步,只是对于水壶而言。
  1. 普通水壶,同步;响水壶,异步。
  2. 虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
  3. 同步只能让调用者去轮询自己,才能提高尽量效率(情况2中),造成老张效率的低下。
 
  所谓阻塞非阻塞,仅仅对于老张而言。
  1. 立等的老张,阻塞;看电视的老张,非阻塞。
  2. 情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于一直在等待的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
 

三 网络中请求

  1. 阻塞非阻塞表示下面 买书过程中 可能出现的状态,是从 这个单进程角度来看待这个买书这个问题。
  2. 同步异步表示一种协作方式,是从全局更高的角度 “进程之间 合作的方式” 来看待买书这个业务。两个进程之间如果商量采用异步方式处理买书这一业务,就不存在阻塞这种状态。

  A.概念

  阻塞非阻塞: 请求不能立即得到应答,需要等待,那就是阻塞;否则可以理解为非阻塞。

  同步异步: 某业务需要甲乙甚至多方合作的时候,

  1. 总是按照“甲方请求一次,乙方应答一次”这样的有序序列处理业务,只有当“一次请求一次应答”的过程结束才可以发生下一次的“一次请求一次应答”,那么就说他们采用的是同步。(同步IO中对同一个描述符的操作必须是有序的

  2. 如果甲方只要有需要,就会发送请求,不管上次请求有没有得到乙方应答。而乙方只要甲方有请求就会接受,不是等这次请求处理完毕再接受甲方新请求。这样请求应答分开的序列,就可以认为是异步。异步情况下,请求和应答不需要一致进行,可能甲方后请求的业务,却先得到乙方的应答。同步是线性的,而异步可以认为是并发的。(异步IO中,异步IO可以允许多方同时对同一个描述符发送IO请求,或者一次发多个请求,当然有机制保证如何区分这些请求,

  3. 我去买一本书,立即买到了,或者没有就走了,这就是非阻塞;(编程中设置IO成非阻塞,返回后再去检查描述符,或者等待通知,然后再去读取。相当于老板告诉我可以先忙点别的,过一会再来问问,或者老板通知我。但期间这个窗口(文件描述符)别人是用不了的)("立即买到了"在IO中也需要等待,不能算非阻塞IO)

  4. 如果恰好书店没有,我就等一直等到书店有了这本书买到了才走,这就是阻塞;而排在我后面的人呢只有我买到了书后才能再买书了。

  5. 如果书店恰好没有,我就告诉书店老板,书来了告诉我一声让我来取或者直接送到我家,然后我就走了,去做别的事了,这就是异步。这时候如果很多人来买书,都是老板登记一下完事。 (从IO角度来说,“告诉我来取”,这个近似于信号驱动IO,不能算异步IO。必须书送到我家才算是异步,如果不送到我家,我想看这本书之前,终究还是需要我跑一趟)

 

四 Java程序中的同步异步

  Java中同步异步和多线程相关,同步的方法可以认为是线程安全的,同样也是效率低得一类。异步的方式大多数是线程不安全的。

  经常看到介绍 ArrayList 和HashMap是异步,Vector和HashTable是同步,这里同步是线程安全的,异步不是线程安全的,举例说明:

    当创建一个Vector对象时候: Vector ve=new Vector();

    ve.add("1");(方法返回之前,其他线程不能操作ve对象)

    当在多线程程序中,第一个线程调用修改对象ve的时候,就为其上了锁,其他线程只有等待。

 

    当创建一个ArrayList对象时候:ArrayList list=new ArrayList();

    list.add("1"); (方法返回之前,其他线程可以操作list对象,例如list.add("2"))

    当在多线程程序中,第一个线程调用修改对象list的时候,没有为其上锁,其他线程访问时就会出现数据异常。

 

  1. 同步(Sync)

    A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求 不到,怎么办,A线程只能等待下去

  2. 异步(Async)
    A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程 仍然请求的到,A线程无需等待
 

  显然,同步最最安全,最保险的。而异步不安全,容易导致死锁,这样一个线程死掉就会导致整个进程崩溃,但没有同步机制的存在,性能会有所提升

  java中实现多线程

  1)继承Thread,重写里面的run方法

  2)实现runnable接口

 比较推荐后者,第一,java有单继承的限制,第二,还可以隔离代码

  线程池 要知道在计算机中任何资源的创建,包括线程,都需要消耗系统资源的。在WEB服务中,对于web服 务器的响应速度必须要尽可能的快,这就容不得每次在用户提交请求按钮后,再创建线程提供服务 。为了减少用户的等待时间,线程必须预先创建,放在线程池中,线程池可以用HashTable这种数 据结构来实现,看了Apach HTTP服务器的线程池的源代码,用是就是HashTable,KEY用线程对象, value 用ControlRunnable,ControlRunnable是线程池中唯一能干活的线程,是它指派线程池中的 线程对外提供服务。 出于安全考虑,Apach HTTP服务器的线程池它是同步的。听说weblogic有异步的实现方式,没有研 究过,不敢确定

--------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------

   

  一、关键字:

  thread(线程)、thread-safe(线程安全)、intercurrent(并发的) synchronized(同步的)、asynchronized(异步的)、 volatile(易变的)、atomic(原子的)、share(共享)

  

  二、概念:

  1、 什么时候必须同步?什么叫同步?如何同步?

  要跨线程维护正确的可见性,只要在几个线程之间共享非 final 变量,就必须使用 synchronized(或 volatile)以确保一个线程可以看见另一个线程做的更改。为了在线程之间进行可靠的通信,也为了互斥访问,同步是必须的。这归因于java语言规范的内存 模型,它规定了:一个线程所做的变化何时以及如何变成对其它线程可见。

  因为多线程将异步行为引进程序,所以在需要同步时,必须有一种方法强制进行。例如:如果2个线 程想要通信并且要共享一个复杂的数据结构,如链表,此时需要确保它们互不冲突,也就是必须阻 止B线程在A线程读数据的过程中向链表里面写数据(A获得了锁,B必须等A释放了该锁)。

  为了达到这个目的,java在一个旧的的进程同步模型——监控器(Monitor)的基础上实现了一个巧 妙的方案:监控器是一个控制机制,可以认为是一个很小的、只能容纳一个线程的盒子,一旦一个 线程进入监控器,其它的线程必须等待,直到那个线程退出监控为止。通过这种方式,一个监控器 可以保证共享资源在同一时刻只可被一个线程使用。这种方式称之为同步。(一旦一个线程进入一 个实例的任何同步方法,别的线程将不能进入该同一实例的其它同步方法,但是该实例的非同步方 法仍然能够被调用)。

  同步和多线程关系:没多线程环境就不需要同步;有多线程环境也不一定需要同步。

  锁提供了两种主要特性:互斥(mutual exclusion)和可见性(visibility)。

  互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议 ,这样,一次就只有一个线程能够使用该共享数据。

  可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个 线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前 的值或不一致的值,这将引发许多严重问题

  小结:为了防止多个线程并发对同一数据的修改,所以需要同步,否则会造成数据不一致(就是所 谓的:线程安全。如java集合框架中Hashtable和Vector是线程安全的。我们的大部分程序都不是线 程安全的,因为没有进行同步,而且我们没有必要,因为大部分情况根本没有多线程环境)。


  未完。待续. . .
 
 
 
参考:https://www.zhihu.com/question/19732473
参考:http://www.cnblogs.com/mengyuxin/p/5358364.html
posted @ 2016-11-03 21:27  qtyy  阅读(879)  评论(0编辑  收藏  举报