非阻塞算法(non-blocking algorithms)定义
 
     所谓非阻塞算法是相对于锁机制而言的,是指:一个线程的失败或挂起不应该引起另一个线程的失败或挂起的一种算法。一般是利用硬件层面支持的原子化操作指令来取代锁的,比如CAS(compare and swap),从而保证共享数据在并发访问下的数据一致性。
 
由AtomicInteger的线程安全机制说起
 
AtomicInteger内部是如何保证线程同步的呢?我们先看AtomicInteger内部的一个典型的方法:
     public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
   }
     public final boolean compareAndSet (int expect, int update) {
       return unsafe.compareAndSwapInt(thisvalueOffset, expect, update);
   }
 
解释:compareAndSet方法内部是调用Java本地方法compareAndSwapInt来实现的,而compareAndSwapInt方法内部又是借助C来调用CPU的底层指令来保证在硬件层面上实现原子操作的。在intel处理器中,CAS是通过调用cmpxchg指令完成的。这就是我们常说的CAS操作(compare and swap)。CAS操作很容易理解,一般来说有三个值:内存值V,期望值A,更新值B,如果内存值V和期望值A相等,那么就用更新值B替换内存值,否则什么都不做。想象一下,假设AtomicInteger当前的value值为1,某个线程A正在执行上述的getAndSet方法,当执行到compareAndSet方法的时候,被另一个线程B抢占了,线程B成功将内存值更新为2,然后轮到线程A来继续执行上述还没有执行完的比较并更新操作,由于线程A上次获得到的current值是1,然后开始执行compareAndSet方法(最后交由CPU的原子执行来执行的),comareAndSet方法发现当前内存值V=2,而期望值A=1(current变量值),所以就不会产生值交换,然后继续下一次重试,在没有别的线程抢占的情况下,下一个循环(在并发很高的情况下可能经过更多次的循环)线程A就能够设置成功,如果线程A是在还没有运行int current = get()这一行操作时被抢占了,那么线程B运行完毕后,线程A获得的将是线程B修改后的值然后进行CAS操作可能就一次成功(在没有其他线程抢占的情况下)。因此,CAS Try-Loop操作能够很好的提供线程同步机制,我们又将此实现过程称之为线程同步的无阻塞算法,又叫”CAS循环”,”lock-free“或”wait-free“算法。
 
非阻塞算法的优点
 
  在java5.0版本时,我们只能通过synchronized来实现线程的同步,synchronized是一种独占锁,独占锁是一种悲观锁,当一个线程访问共享资源的时候,其他线程必须处在阻塞状态,只有在拥有锁的线程释放锁以后才能被其他线程锁竞争,JVM 实现阻塞的方式通常是挂起阻塞的线程,过一会儿再重新调度它。由此造成的上下文切换相对于锁保护的少数几条指令来说,会造成相当大的延迟,这将引起性能问题,所以我们称这种锁为重量级锁。所谓乐观锁就是指:对竞争资源不用加锁,而是假设没有冲突去完成某项操作,如果因为冲突失败就不断重试,直到成功为止。上面所说的循环CAS操作就是上述所说的乐观锁。
  1. 利用硬件的原生支持代替JVM对代码路径的锁定,从而提供更细粒度(独立的内存地址)的同步。
  2. 失败的线程可以立即重试而不用被挂起,降低了争用成本,即使有少量失败的CAS操作,也依然锁争用造成的重新调度快的多
  3. 争用CAS提供更短的延迟(因为争用CAS比争用锁会更快),提供更好的吞吐率。
  4. 对生存问题(死锁和线程优先级反转)提供更好的防御
在轻度到中度的争用情况下阻塞算法的性能会超越阻塞算法,因为 CAS 的多数时间都在第一次尝试时就成功,而发生争用时的开销也不涉及线程挂起和上下文切换,只多了几个循环迭代。没有争用的 CAS 要比没有争用的锁便宜得多(这句话肯定是真的,因为没有争用的锁涉及 CAS 加上额外的处理),而争用的 CAS 比争用的锁获取涉及更短的延迟。在高度争用的情况下(即有多个线程不断争用一个内存位置的时候),基于锁的算法开始提供比非阻塞算法更好的吞吐率,因为当线程阻塞时,它就会停止争用,耐心地等候轮到自己,从而避免了进一步争用。但是,这么高的争用程度并不常见,因为多数时候,线程会把线程本地的计算与争用共享数据的操作分开,从而给其他线程使用共享数据的机会。
 
参考:

基于CAS操作的非阻塞算法的更多相关文章

  1. Java锁与非阻塞算法的性能比较与分析+原子变量类的应用

    15.原子变量与非阻塞同步机制 在java.util.concurrent包中的许多类,比如Semaphore和ConcurrentLinkedQueue,都提供了比使用Synchronized更好的 ...

  2. 《java并发编程实战》读书笔记12--原子变量,非阻塞算法,CAS

    第15章 原子变量与非阻塞同步机制 近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令(例如比较并交换指令)代替锁老确保数据在并发访问中的一致性. 15.1 锁的劣势 ...

  3. Java 理论与实践: 非阻塞算法简介——看吧,没有锁定!(转载)

    简介: Java™ 5.0 第一次让使用 Java 语言开发非阻塞算法成为可能,java.util.concurrent 包充分地利用了这个功能.非阻塞算法属于并发算法,它们可以安全地派生它们的线程, ...

  4. Java 理论与实践: 非阻塞算法简介--转载

    在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是synchronized 关键字(也称为内在锁),它强制实行互斥,确保 ...

  5. java并发之非阻塞算法介绍

    在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在算法中如果一个线程的挂起没有导致其它的线程挂起,我们就说这个算法是非阻塞的. 为了更好的理解阻塞算 ...

  6. 29、Java并发性和多线程-非阻塞算法

    以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在 ...

  7. Java 理论与实践-非阻塞算法简介

    在不只一个线程访问一个互斥的变量时,所有线程都必须使用同步,否则就可能会发生一些非常糟糕的事情.Java 语言中主要的同步手段就是 synchronized 关键字(也称为内在锁),它强制实行互斥,确 ...

  8. 非阻塞算法(Lock-Free)的实现

    目录 非阻塞的栈 非阻塞的链表 非阻塞算法(Lock-Free)的实现 上篇文章我们讲到了使用锁会带来的各种缺点,本文将会讲解如何使用非阻塞算法.非阻塞算法一般会使用CAS来协调线程的操作. 虽然非阻 ...

  9. 《Java并发编程实战》笔记-非阻塞算法

    如果在某种算法中,一个线程的失败或挂起不会导致其他线程也失败和挂起,那么这种算法就被称为非阻塞算法.如果在算法的每个步骤中都存在某个线程能够执行下去,那么这种算法也被称为无锁(Lock-Free)算法 ...

随机推荐

  1. 2018.06.29 NOIP模拟 排列(线段树)

    排列(premu.cpp) [题目描述] 对于一个 1 到 n 的排列,逆序数的定义为:排列中第 i 位 ai的逆序数就是 a1-ai-1中比 ai大的数的个数.另外用 pi表示 a1,-,ai的逆序 ...

  2. FreeTextBox备忘

    添加对4.0的dll文件引用 吧aspnet_client目录 copy到根目录下 设置文件上传目录属性ImageGalleryPath 设置相册属性到 ftb.imagegallery.aspx位置 ...

  3. Go语言的传参和传引用[转]

    目录[-] 传参和传引用的问题 传slice不是传引用! 什么叫传引用? 为什么传slice不是传引用? 为什么很多人误以为slice是传引用呢? 传指针和传引用是等价的吗? 所有类型的函数参数都是传 ...

  4. 记spring mvc传入List<Object>的一次尝试

    首先,看一段异常: org.springframework.http.converter.HttpMessageNotReadableException: Could not read documen ...

  5. 201709011工作日记--Volley源码详解(三)

    1. RequestQueue类 我们使用 Volley 的时候创建一个 request 然后把它丢到 RequestQueue 中就可以了.那么来看 RequestQueue 的构造方法,含有四个参 ...

  6. 命令行中开启mySQL数据库服务

    sudo /Applications/XAMPP/xamppfiles/bin/mysql.server start  

  7. faceswap安装说明

    Installing Faceswap Installing Faceswap Prerequisites Hardware Requirements Supported operating syst ...

  8. MySQL 分表和分区

    1.为什么需要分表和分区 在开发的过程中,经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,如果涉及联合查询的情况,性能更加 ...

  9. nodejs+express安装配置(Linux版本)

    在ubuntu下面,直接从源里面安装nodejs的话,此版本还行,但是相关的express等,会比较老. 采用源码安装,先下载nodejs的源码,然后三步: ./configure make make ...

  10. PostgreSQL 用户和权限管理

    PostgreSQL 用户和权限管理 创建 CREATE ROLE rolename;CREATE USER username;CREATE USER和CREATE ROLE的区别在于,CREATE ...