在JDK1.5之前。Java主要靠synchronized这个关键字保证同步,已解决多线程下的线程不安全问题,但是这会导致锁的发生,会引发一些个性能问题。

锁主要存在一下问题

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

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

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

Volatile是一个不错的选择,但是前面我们已经说了,volatile不能保证原子性,因此同步还是需要用到锁。

也许大家已经听说过,锁分两种,一个叫悲观锁,一种称之为乐观锁。Synchronized就是悲观锁的一种,也称之为独占锁,加了synchronized关键字的代码基本上就只能以单线程的形式去执行了,它会导致其他需要该资源的线程挂起,直到前面的线程执行完毕释放所资源。而另外一种乐观锁是一种更高效的机制,它的原理就是每次不加锁去执行某项操作,如果发生冲突则失败并重试,直到成功为止,其实本质上不算锁,所以很多地方也称之为自旋。

乐观锁用到的主要机制就是CAS。Compare And Swap。

CAS有三个操作数,内存数据v,旧的预期数据A,要修改的数据B。每次进行数据更新时,当且仅当预期值A和内存中的数据V相同时,才将内存中的数据修改为B,否则什么也不做。

使用这种机制编写的算法也叫非阻塞算法,标准定义为一个线程的失败或者挂起不影响其他线程的失败或者挂起的算法。

现在的CPU提供了特殊的指令来自动更新共享数据,而且能检测到其他数据的干扰,因此可以通过compareAndSet来提到锁定。前面我们提到的一些原子类其实就是用的这个原理,如AtomicInteger,我们来看一下它对应的源码。

private volatile int value;

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

这里很显然采取了CAS的机制,每次从内存中读取数据都需要和+1后的数据进行一次CAS操作,如果成功返回结果,否则就失败重试,直到重试成功为止!

但是方法compareAndSet却是利用JNI来完成CPU指令的操作。

我们来看一下对应的源码

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe(); public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

整个过程就是这样子的,利用CPU的CAS机制,同时借助JNI来完成Java的非阻塞算法。基本上Java中的原子类都是使用类似的机制来保证数据的原子操作的。

尽管CAS机制使得我们可以不依赖同步,不影响和挂起其他线程实现原子性操作,能大大提升运行时的性能,但是会导致一个ABA的问题。如线程一和线程二都取出了主存中的数据为A,这时候线程二将数据修改为B,然后又修改为A,此时线程一再次去进行compareAndSet的时候仍然能够匹配成功,而实际对的数据已经发生了变更了,只不过发生了2次变化将对应的值修改为原始的数据了,并不代表实际数据没有发生变化。这时候前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。这允许一对变化的元素进行原子操作。

后续我们将继续探讨一下锁机制的实现,Java并发包中的锁是如何保证操作正确性的同时又大幅提升性能的。

深入浅出Java并发包—CAS机制的更多相关文章

  1. 深入浅出Java并发包—锁机制(二)

    接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tr ...

  2. 深入浅出Java并发包—锁机制(三)

    接上文<深入浅出Java并发包—锁机制(二)>  由锁衍生的下一个对象是条件变量,这个对象的存在很大程度上是为了解决Object.wait/notify/notifyAll难以使用的问题. ...

  3. 深入浅出Java并发包—锁机制(一)

    前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...

  4. 深入浅出Java并发包—CountDownLauch原理分析 (转载)

    转载地址:http://yhjhappy234.blog.163.com/blog/static/3163283220135875759265/ CountDownLauch是Java并发包中的一个同 ...

  5. 深入浅出Java并发包—锁(Lock)VS同步(synchronized)

    今天我们来探讨一下Java中的锁机制.前面我们提到,在JDK1.5之前只能通过synchronized关键字来实现同步,这个前面我们已经提到是属于独占锁,性能并不高,因此JDK1.5之后开始借助JNI ...

  6. 深入浅出Java并发包—指令重排序

    前面大致提到了JDK中的一些个原子类,也提到原子类是并发的基础,更提到所谓的线程安全,其实这些类或者并发包中的这么一些类,都是为了保证系统在运行时是线程安全的,那到底怎么样才算是线程安全呢? Java ...

  7. 深入浅出 Java Concurrency (15): 锁机制 part 10 锁的一些其它问题

      主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的 ...

  8. 深入浅出 Java Concurrency (7): 锁机制 part 2 AQS

      在理解J.U.C原理以及锁机制之前,我们来介绍J.U.C框架最核心也是最复杂的一个基础类:java.util.concurrent.locks.AbstractQueuedSynchronizer ...

  9. 深入浅出 Java Concurrency (15): 锁机制 part 10 锁的一些其它问题[转]

    主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的一点 ...

随机推荐

  1. oracle 备份与还原 及相关操作

    drop user 用户名 cascade; ........删除用户 create user 用户名 identified by 密码 default tablespace 数据文件名 tempor ...

  2. float闭合(清除浮动)和CSS HACK

    一.float 闭合(清除浮动) 将以下代码加入Global CSS 中,给需要闭合的div加上 class="clearfix" 即可,屡试不爽. <style>.c ...

  3. svn迁移

    一.       VisualSVN服务器迁移 场景:把A服务器备份到B服务器 方法: 1.拷贝A上Repositories文件夹到B上 2.打开B上VisualSVN Server Manager ...

  4. HTML5读取本地文件 FileReader API接口

    1.FileReader接口的方法 FileReader接口有4个方法,其中3个用来读取文件,另一个用来中断读取.无论读取成功或失败,方法并不会返回读取结果,这一结果存储在result属性中. Fil ...

  5. django post报403问题

    第一个问题是: 我使用jquery的ajax向后台传值, 当使用GET方法时没问题 $.ajax({ type:"GET" url: data: success: }) 但是由于基 ...

  6. 转载---linux运维相关

    前段时间,我在准备面试的时搜到的一套Linux运维工程师面试题,感觉比较全面,一直保存在草稿,刚在整理后台时翻了出来,干脆就发出来好了,以备不时之需. 1.linux如何挂在windows下的共享目录 ...

  7. Linux挂载60T存储

    操作系统: CentOS 6.3 存储:总大小为72T,并划分成3个块,每块20T 安装多实例MySQL数据库,不想挂载3个块,弄成一个大的比较方便管理,个人比较懒. 配置多路径:http://blo ...

  8. Java线程通信——wait() 和 notify()

    Object类中有关线程通信的方法有两个notify方法和三个wait方法,官方解释: void notify() Wakes up a single thread that is waiting o ...

  9. c++ memset 函数 及 坑

    #include <string.h> #include <stdio.h> typedef struct ss{ int num; ][]; }tent; tent a; i ...

  10. 《head first java 》读书笔记(二)

    Updated 2014/03/27 P402-P454 Updated 2014/04/03 P454- 世界三大首席管理器: border, flow, box borderLayout: 五个区 ...