一般认为并发可以分为阻塞与非阻塞,对于非阻塞可以进一步细分为无障碍、无锁、无等待,下面就对这几个并发级别,作一些简单的介绍。

1、阻塞

阻塞是指一个线程进入临界区后,其它线程就必须在临界区外等待,待进去的线程执行完任务离开临界区后,其它线程才能再进去。

2、无障碍(obstruction-free)

  • 无障碍是一种最弱的非阻塞调度
  • 自由出入临界区
  • 无竞争时,有限步内完成操作
  • 有竞争时,回滚数据

无障碍并不要求一个线程进入临界区之后,其它线程在临界区外等待。所有线程都可以自由地进入临界区,它是一种最弱的非阻塞调度。

跟阻塞调度比较,阻塞调度可以认为是一种悲观的策略,它会认为多个线程一起修改数据会使数据损坏,所以阻塞调度每次只能允许一个线程去修改数据。而非阻塞调度相对来说比较乐观,它认为如果多个线程一起修改也未必会把造成数据损坏,所以它允许多个线程同时进入临界区,但无障碍是一种宽进严出的策略,进的时候不作限制,所有的线程都能进入临界区做其想做的事情,包括读与写,但是出来的时候就不那么宽松了,如果一个线程在临界区中的操作遇到了数据竞争,跟其它线程产生了冲突,它就会回滚这条数据,然后重试自己的操作。比如读取x与y的值,这个操作是分步进行的先读x,再读y,当读完x,发现别的线程修改了y,再读y就已经没有意义了,因为可能会读到一个错误的数据,所以该线程会重试,再去读取一次,直到自己读到的x、y没有问题为止,所以无障碍是一种会不断重试的调度策略,但它会保证没有数据竞争时,线程必然能在有限的步骤内执行完任务。

在无障碍的调度方式当中,所有的线程都相当于在拿取一个系统当前的快照,它们会一直重试,直到拿到的快照有效为止。

3、无锁(lock-free)

  • 是无障碍的
  • 保证有一个线程可以胜出

前面说的无障碍是指所有的线程都能进入临界区,但如果发生了竞争,无障碍并不保证临界区的线程能够顺利的出来,因为如果线程发现自己的数据每次去读取或者去操作,总是跟其它线程产生冲突,它就会不停地重试,如果在临界区当中有10个线程,线程1修改了部分数据,结果它被线程2干扰了,线程2又被线程3干扰,依此类推,最后线程1它又可能去干扰线程10,如果它们之间是彼此干扰的,最终会导致所有的线程都卡死在里面,系统的性能会受到比较严重的影响,因此,无锁必须在无障碍的基础上加一个约束,保证在竞争当中有一个线程是必然能够胜出的,这样就能保证在临界区的线程当中至少有一个是能顺利走出去的,而不至于全部在里面阵亡掉,如果至少有一个线程能够出去,那么就有第二个线程能够出去,假设里面有一百个线程,第一个线程竞争胜利,走出了临界区,剩下99个再竞争又必然能胜利一个,因为每次竞争它必然保证能有一个胜利,使得系统至少是能够顺畅的执行下去的,这就是无锁,下面这段代码在java当中是比较典型的使用无锁的代码:

while(!hyes.compareAndSet(localHyes,localHyes+1)){
localHyes = hyes.get();
}

4、无等待(wait-free)

  • 无锁的
  • 要求所有的线程都必须在有限步内完成
  • 无饥饿的

前面说了无锁是能保证至少有一个线程能够在有限步当中完成它的操作,所有的线程在不停地竞争直到有一个胜出为止。无等待相比于无锁更进一步,它首先要求是无锁的,保证所有线程能进并且至少有一个线程能出来,同时无等待它在提高要求,它要求所有进入临界区的线程都能够在有限步当中完成其操作,这个要求很高,因为任何线程都能够无障碍进入临界区,并且任何线程都能够在有限步当中完成操作后离开临界区,这就会使得整个系统的运行变得非常顺畅,无等待可以说是并行最高级别了,它基本上能使整个系统发挥到最好佳效率。

无等待必须然也是无饥饿的,因为所有的线程都能在有限步当中完成,因此必然不会有线程永久地呆在临界区内出不去,所以它一定是无饥饿的。

无等待的一个典型案例是,有读写两个线程,如果说只有读线程没有写线程,那么所有的读线程之间必然是无等待的,因为读不会修改数据,如果有一个写线程在里面,由于会修改数据 ,写线程必然会导致读线程不是无等待。因此可以提出一种算法去作一点改进,比如说有一种算法它会这样做,因为写可能会影响到读,所以每次写之前先把数据拷贝一份副本,线程修改的是这个副本而非原始数据,修改数据的过程可能需要一点时间,因为修改的是副本数据而不是原始数据,所以这个修改的过程也不影响线程读,因此在这个过程当中所有的读线程一样是无等待的,它们都能够在有限的步骤当中完成自己的操作,而所有的写线程相对来讲,因为每个写线程它都是写自己的副本,因此它们的写也是无等待的,所以它们都不需要去跟彼此作同步,最后需要同步的只是将写完之后的数据覆盖原始数据,而这个覆盖原始数据的动作是非常快的,因为我们并不需要作大量的写操作,只不过是一个指针或引用作一个替换而已,不管哪个写线程胜出,总是能够保证替换上去的数据是一致的,并不像其它的算法一样,可能会把数据写坏,因为大家都写的是副本,最后是一个指针指向谁的问题,这样数据必然是安全的,这种方式它就是无等待的一个典型的实现。

无等待的实现,在所有并发等级中是最麻烦的,而且技巧性的东西会比较多,相对来说,无锁的使用会更加广泛一些。

转载出处:http://www.hao124.net/article/28

java高并发程序设计模式-并发级别:阻塞、无障碍、无锁、无等待【转载】的更多相关文章

  1. 使用Interlocked在多线程下进行原子操作,无锁无阻塞的实现线程运行状态判断

    巧妙地使用Interlocked的各个方法,再无锁无阻塞的情况下判断出所有线程的运行完成状态. 昨晚耐着性子看完了clr via c#的第29章<<基元线程同步构造>>,尽管这 ...

  2. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. java并发:AtomicInteger 以及CAS无锁算法【转载】

    1 AtomicInteger解析 众所周知,在多线程并发的情况下,对于成员变量,可能是线程不安全的: 一个很简单的例子,假设我存在两个线程,让一个整数自增1000次,那么最终的值应该是1000:但是 ...

  4. Java高并发之设计模式

    本文主要讲解几种常见并行模式, 具体目录结构如下图. 单例 单例是最常见的一种设计模式, 一般用于全局对象管理, 比如xml配置读写之类的. 一般分为懒汉式, 饿汉式. 懒汉式: 方法上加synchr ...

  5. 【MPI学习4】MPI并行程序设计模式:非阻塞通信MPI程序设计

    这一章讲了MPI非阻塞通信的原理和一些函数接口,最后再用非阻塞通信方式实现Jacobi迭代,记录学习中的一些知识. (1)阻塞通信与非阻塞通信 阻塞通信调用时,整个程序只能执行通信相关的内容,而无法执 ...

  6. 高级java高并发,高性能,分布式,高可用,负载均衡,系统架构实战

    java架构师.集群.高可用.高可扩 展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布 式项目实战 视频课程包含: ...

  7. Java高并发程序设计学习笔记(一):并行简介以及重要概念

    转自:https://blog.csdn.net/dataiyangu/article/details/86211544#_28 文章目录为什么需要并行?反对意见大势所趋几个重要的概念同步(synch ...

  8. 《实战Java高并发程序设计》读书笔记四

    第四章 锁的优化及注意事项 1.锁性能的几点建议 减小锁持有时间: 系统持有锁时间越长锁竞争程度就越激烈,只对需要同步的方法加锁,可以减小锁持有时间进而提高锁性能. 减少锁的持有时间有助于降低锁冲突的 ...

  9. java多线程中的死锁、活锁、饥饿、无锁都是什么鬼?

    死锁.活锁.饥饿是关于多线程是否活跃出现的运行阻塞障碍问题,如果线程出现了这三种情况,即线程不再活跃,不能再正常地执行下去了. 死锁 死锁是多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而又 ...

随机推荐

  1. jquery-load()方法

    调用load方法的完整格式是:load( url, [data], [callback] ), 其中: •url:是指要导入文件的地址. •data:可选参数:因为Load不仅仅可以导入静态的html ...

  2. typeof运算符

    javascript中typeof用来判断一个变量或表达式的数据类型. typeof 返回值有六种可能: "number," "string," "b ...

  3. ES6基础知识汇总

    1.如何理解ECMAScript6? ECMAScript是什么,ECMASCript的作用 2.新增let关键字 let的用途 3.关键字const const作用,传址赋值 4.解构赋值 解构赋值 ...

  4. Django之利用ajax实现图片预览

    利用ajax实现图片预览的步骤为: 程序实现的方法为: 方法一: upload.html <!DOCTYPE html> <html lang="en"> ...

  5. 一键切图 PS 动作 【收藏】

    使用方法 一键切图动作.zip 1. 下载动作 2. 打开PS 动作 窗口,导入动作 3. 选中图层后 点击 F2 一键切图 详情看原文链接 原文链接

  6. 47、求1+2+3+...+n

    一.题目 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 二.解法 public class Solut ...

  7. SPOJ DQUERY D-query (在线主席树/ 离线树状数组)

    版权声明:本文为博主原创文章,未经博主允许不得转载. SPOJ DQUERY 题意: 给出一串数,询问[L,R]区间中有多少个不同的数 . 解法: 关键是查询到某个右端点时,使其左边出现过的数都记录在 ...

  8. php直接输出json格式

    php直接输出json格式,很多新手有一个误区,以为用echo json_encode($data);这样就是输出json数据了,没错这样输出文本是json格式文本而不是json数据,正确的写法是应该 ...

  9. curd 插件

    1. Django项目启动 自动加载文件 制作启动文件 . 注册strak 在apps.py 类里面增加如下 def ready(self): from django.utils.module_loa ...

  10. Eolinker——代码注入插入随机参数值

    因为目前eolinker的API自动化测试不支持“构造参数”,所以用到随机数时,可使用代码注入的方式来实现 分步指南 示例:“重置密码”接口,每次运行重置的密码要求不重复 再此接口的“代码注入”区域写 ...