同步与异步,阻塞与非阻塞的关系

这几个知识点其实用处很多,也是很容易搞混的,我们平时可能用了但是并没有发现,今天想来总结一下。
我第一次感受到同步异步的不同是在学习volatile的时候(因为我之前也没有多线程概念),之前也被同步啊这些概念搞的很乱。当我主线程新建了一个子线程之后两个线程是同时运行的,当时我才发现原来他们不是串行的,献丑了。好了进入正题。

从总体来看,同步和异步的概念是大于阻塞与非阻塞,并且他们之间没有对应关系。也就是说同步不一定是阻塞,异步也不一定是非阻塞的。阻塞和非阻塞只是一种性质,而同步和异步是从宏观上对一种任务性质的描述。
同步:如果有多个任务要发生,这些任务必须是挨个执行的,一个子事件或子任务的执行或导致主事件流的暂停或等待,这些事件不能并发的执行。
异步:多个任务或事件可以并发地执行,一个事件或任务的执行不会导致整个流程的暂停或等待。
最直观的来看:

void function(){
  fun1();
  fun2();
}

那么fun1()和fun2()就是串行运行,站在任务的角度我们可以说他们是同步的。(这里大家可以想一下多线程同步是要干什么?结合关于临界区的那个概念,我们的目的不就是要保证多个线程在访问临界区的时候要一个一个访问吗,要串行化,不能并发的访问。其实这就是我们多线程要解决的核心问题)

void function(){
  new Thread(){
    public void run(){
      fun1();
    }
  }.start();
  new Thread(){
    public void run(){
      fun2();
    }
  }.start();
 }

那么此时fun1()和fun2()就是并行执行的,因为多线程的本质就是并发运行。而且我们也可以通过thread.join()方法来让线程同步运行。
咱们再来点大白话的解释,比如任务A中间有一个子任务B,当执行B的时候A如果处于等待状态,那么就是同步的。如果A告诉B后,就由另一个线程去执行B,到时候执行完了自动返回给A的话,那么就是异步的。

阻塞和非阻塞可以看做一种性质,既然是性质,那么很多情况下都会存在。(可以多和io结合起来考虑)
阻塞:当某个事件或任务在执行过程中发出了一个请求操作,但是由于该请求操作需要的条件不满足,就会一直等在那,直到条件满足。比如我们都用过的:InputStream.read()/socket.read()/object.wait()等等,如果请求的资源没有准备好,那么请求的线程就阻塞在那了,只有等接受到数据之后才继续执行。
非阻塞:当某个事件或任务在执行过程中发出了一个请求操作后,但是由于该请求操作需要的条件不满足,那么被请求端会发出一个信息,告诉请求资源的线程,我这边资源没准备好,你可以去做其他的。
所以,其实阻塞和非阻塞咱们可以理解为,A请求B之后,在B端资源没准备好的时候,B是让A一直等待还是发出一个标志。

一定记住,这两组是完全不同的概念。同步不能和阻塞画等号(只能说同步往往是阻塞的),是不是阻塞咱们可以通过判断此刻主线程处于什么状态来判断。比如:在NIO中,我们有一种的多路复用模式。概念就是:我们不是采用一个线程去负责一个socket的方式,如果那样一定是阻塞的。我们采用一个线程管理多个socket的方式,该线程不断轮询所有socket,我们叫做Selector,通过select()方法,我们可以找到某一时刻真正需要io资源的socket,避免资源浪费。那么此时,我们的主线程是一直处于running状态的,因为他在不断轮询,但它同时又是非阻塞的,因为没有处于等待状态嘛。
同时异步也不一定是非阻塞的(只能说异步常常是非阻塞的),例如我们往线程池里面提交任务的时候,调用Future.get()来试图获取处理结果时,由于结果还没有运行出来,所以该方法会被阻塞。但是从多线程角度来看,此时它是异步执行的。
再用大白话解释一下,不敢保证一定准确无误,同步和异步我们描述的是多个线程之间的关系,而阻塞和非阻塞描述的是一个线程的属性。我们既然选择了多线程也就选择了异步编程的方式,但是却要解决同步的问题,而同步往往意味着性能的缺失和安全性的保障。而对于阻塞和非阻塞来说,传统的方式大多是阻塞的,如果采用异步非阻塞往往意味着更多的代码和资源投入,很多时候需要工作者线程甚至jvm底层的帮助

猜你喜欢

转载自blog.csdn.net/pilipaladuang/article/details/80381216