在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. 一款仿PBA官网首页jQuery焦点图的切换特效

    一款仿PBA官网首页jQuery焦点图的切换特效,非常的简单大方, 在对浏览器兼容性的方面做了不少的功夫.IE6也勉强能过去. 还是一款全屏的焦点图切换特效.大气而清新.很适合简介大方的网站. 下图还 ...

  2. mac OS X下git代码行统计命令

    1.统计某人的代码提交量,包括增加,删除 git log --author=-- --until=-- --pretty=tformat: --numstat | awk '{ add += $1 ; ...

  3. 【linux】iptables 开启80端口

    经常使用CentOS的朋友,可能会遇到和我一样的问题.开启了防火墙导致80端口无法访问,刚开始学习centos的朋友可以参考下. 经常使用CentOS的朋友,可能会遇到和我一样的问题.最近在Linux ...

  4. iOS实现图片的缩放和居中显示

    直接上代码 // // MoveScaleImageController.h // MoveScaleImage // // Created by on 12-4-24. // Copyright ( ...

  5. 最新区分兼容IE6/IE7/IE8/IE9/FF的CSS HACK写法和Css if hack条件语法操作说明

    自从安装了IE8.0正式版本!木头 就对基本的几个 CSS HACK的做一下归纳!希望对网页前端布局DIV+CSS的实施者有所帮助! 本文就主要以:IE6+IE7+IE8+IE9+FF为主要研究对象 ...

  6. as 和is的区别

    is 1,检查对象的兼容性,并返回true或false 2,不会抛出异常 3,null永远返回false as 1,检查对象的兼容性,并返回 true或false 2,不会抛出异常 3,null将抛出 ...

  7. 在有跳板机的情况下,SecureCRT自动连接到目标服务器

    为了服务器的安全,运维人员经常会要求我们先登录到跳板机,然后再SSH连接到目标服务器.但是这样是很繁琐的,每次在SecureCRT创建一个连接,都需要输入SSH命令,然后输入密码. 下面的方法可以实现 ...

  8. 非阻塞io与记录锁

    非阻塞io 1.对比 阻塞io的例子:scanf从终端获取输入时,如果不输入程序就会一直停在那; 对一个已经有写锁的文件请求读时, 会一直空等直到前面的进程释放锁... 非阻塞的例子:读取文件内容, ...

  9. java实现合并两个已经排序的列表

    相对于C++来说,Java的最大特点之一就是没有令人困惑的指针,但是我们不可否认,在某些特定的情境下,指针确实算的上一把利刃.虽然Java中没有明确定义出指针,但是由于类的思想,我们可以使用class ...

  10. 【js】正则表达式豁然开朗

    http://www.jikexueyuan.com/course/809_3.html?ss=1 小括号,中括号 中括号:[] ,它表示里面的字符任选一个 比如[abcd]+,就表示abcd这四个字 ...