在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)流行的原子

最新文章

  1. Finger Trees: A Simple General-purpose Data Structure
  2. 本地数据库(SQL Server)远程连接服务器端服务器
  3. php中文汉字截取函数
  4. Modoer列表页性能分析及优化
  5. 关于cocos2d安装时编译不成功(个人心得)
  6. log4j使用和配置详解
  7. 福州大学W班-团队作业-随堂小测(同学录)成绩
  8. Linux的chkconfig命令详解
  9. 一个很有趣的示例Spring Boot项目,使用Giraphe CMS和Spring Boot
  10. 不使用SpringBoot如何将原生Feign集成到Spring中来简化http调用
  11. 修改MyEclipse字体大小及颜色
  12. Jenkins Maven Selenium TestNG踩坑记
  13. UVALive 8513 lovers 2017 西安区域赛 B 贪心+multiset
  14. linux之测试硬盘速度篇
  15. DEVC++ C++ Builder6.0
  16. (转)什么是.NET?什么是CLI?什么是CLR?IL是什么?JIT是什么,它是如何工作的?GC是什么,简述一下GC的工作方式?
  17. 分享四个 Linux 上的网络信息嗅探工具
  18. java生成PDF,各种格式、样式、水印都有
  19. Centos 7.0 下安装 Zabbix server 3.0服务器的安装及 监控主机的加入(1)
  20. 使用Zabbix监控rabbitmq服务

热门文章

  1. springboot集成websocket实现向前端浏览器发送一个对象,发送消息操作手动触发
  2. webpack官方文档学习
  3. System.Web.Mvc.IController.cs
  4. Mybatis笔记 – 入门程序开发
  5. JAXB注解使用
  6. DXP 笔记
  7. HDU-1852-Beijing 2008-一个神奇的公式求逆元
  8. Mac 解决硬盘插入不能写的问题
  9. Query Rewrite Plugins
  10. vue中使用动画vue-particles实现背景粒子酷炫效果