在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁)。

锁机制存在以下问题:

(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。

(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。

volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。

独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

CAS 操作

上面的乐观锁用到的机制就是CAS,Compare and Swap。

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

非阻塞算法 (nonblocking algorithms)

一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。

现代的CPU提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。

拿出AtomicInteger来研究在没有锁的情况下是如何做到数据正确性的。

private volatile int value;

首先毫无以为,在没有锁的机制下可能需要借助volatile原语,保证线程间的数据是可见的(共享的)。

这样才获取变量的值的时候才能直接读取。

public final int get() {
        return value;
    }

然后来看看++i是怎么做到的。

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

在这里采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。

compareAndSet利用JNI来完成CPU指令的操作。

public final boolean compareAndSet(int expect, int update) {   
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。

而整个J.U.C都是建立在CAS之上的因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。参考资料的文章中介绍了如果利用CAS构建非阻塞计数器、队列等数据结构。

CAS看起来很爽,但是会导致“ABA问题”。

CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。这允许一对变化的元素进行原子操作。

参考资料:

(1)非阻塞算法简介

(2)流行的原子

深入浅出 Java Concurrency (5): 原子操作 part 4 CAS操作的更多相关文章

  1. 《深入浅出 Java Concurrency》——原子操作

    part1 从AtomicInteger開始 从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包.而Queue.非常多情况下使用到了Atomic操作.因此首 ...

  2. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

  3. 深入浅出 Java Concurrency (5): 原子操作 part 4[转]

    在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁). 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度 ...

  4. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则[转]

    在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到锁机制,因此此小节中会适当引入锁的概念. 在Java Concurrency in Practice中是这样定义线程安全的: ...

  5. 深入浅出 Java Concurrency (3): 原子操作 part 2

    转:http://www.blogjava.net/xylz/archive/2010/07/02/325079.html 在这一部分开始讨论数组原子操作和一些其他的原子操作. AtomicInteg ...

  6. 深入浅出 Java Concurrency (2): 原子操作 part 1

    转:http://www.blogjava.net/xylz/archive/2010/07/01/324988.html 从相对简单的Atomic入手(java.util.concurrent是基于 ...

  7. 深入浅出 Java Concurrency (3): 原子操作 part 2[转]

    在这一部分开始讨论数组原子操作和一些其他的原子操作. AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray的API类似,选择有代表性的Atom ...

  8. 深入浅出 Java Concurrency (2): 原子操作 part 1[转]

    从相对简单的Atomic入手(java.util.concurrent是基于Queue的并发包,而Queue,很多情况下使用到了Atomic操作,因此首先从这里开始).很多情况下我们只是需要一个简单的 ...

  9. [转] 多线程 《深入浅出 Java Concurrency》目录

    http://ifeve.com/java-concurrency-thread-directory/ synchronized使用的内置锁和ReentrantLock这种显式锁在java6以后性能没 ...

随机推荐

  1. 单例模式(Singleton-Pattern)百媚生

    1 动机 对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号 ...

  2. 伪多项式时间 Pseudo-polynomial time

    2018-03-15 14:20:08 伪多项式时间:如果一个算法的传统时间复杂度是多项式时间的,而标准时间复杂度不是多项式时间的,则我们称这个算法是伪多项式时间的. 想要理解“伪多项式时间”,我们需 ...

  3. java中如何使用Junit测试

    java中如何使用Junit测试 一.总结 一句话总结:a.单元测试的测试代码在test文件夹下,和源码不在同一个文件夹下 b.测试的类方法都以test开头,后面接要测试的类或者方法的名字 1.JUn ...

  4. linux挂载windows共享文件夹出错,提示mount error(13): Permission denied

    完整的可以工作的命令行: mount -v -t cifs -o username=clouder,password=123456,iocharset=utf8,sec=ntlm //172.28.1 ...

  5. zoj3261变形并查集

    需要变形的并查集,这题错了好久,一直没a掉,终于在重写第三次的时候a了 先保存数据,把不需要拆分的边合并,逆向计算,需要拆分时就合并,之前不知道为啥写搓了,tle好久 #include<map& ...

  6. Zabbix SNMP OID discovery,自动发现

    Unlike file system and network interface discovery, the item does not necessarily have to have “snmp ...

  7. IOS-组件化架构漫谈

    本文作者: 伯乐在线 - 刘小壮 .未经作者许可,禁止转载!欢迎加入伯乐在线 专栏作者. 前段时间公司项目打算重构,准确来说应该是按之前的产品逻辑重写一个项目

  8. vue2 过渡动画

    <body> <div id="app"> <transition name="move"> // transition里面 ...

  9. java 生成xml文件

    这里也使用的是import org.w3c.dom.Document; 首先创建document对象,给该对象赋值,然后将document对象使用transformer的transformer转换方法 ...

  10. python的单元测试代码编写流程

    单元测试: 单元测试是对单独的代码块分别进行测试, 以确保它们的正确性, 单元测试主要还是由开发人员来做, 其余的集成测试和系统测试由专业的测试人员来做. python的单元测试代码编写主要记住以下几 ...