一、CAS和synchronized适用场景

1、对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

2、对于资源竞争严重的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。以java.util.concurrent.atomic包中AtomicInteger类为例,其getAndIncrement()方法实现如下:

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

如果compareAndSet(current, next)方法成功执行,则直接返回;如果线程竞争激烈,导致compareAndSet(current, next)方法一直不能成功执行,则会一直循环等待,直到耗尽cpu分配给该线程的时间片,从而大幅降低效率。

二、CAS错误的使用场景

 1 public class CASDemo {
2 private final int THREAD_NUM = 1000;
3 private final int MAX_VALUE = 20000000;
4 private AtomicInteger casI = new AtomicInteger(0);
5 private int syncI = 0;
6 private String path = "/Users/pingping/DataCenter/Books/Linux/Linux常用命令详解.txt";
7
8 public void casAdd() throws InterruptedException {
9 long begin = System.currentTimeMillis();
10 Thread[] threads = new Thread[THREAD_NUM];
11 for (int i = 0; i < THREAD_NUM; i++) {
12 threads[i] = new Thread(new Runnable() {
13 public void run() {
14 while (casI.get() < MAX_VALUE) {
15 casI.getAndIncrement();
16 }
17 }
18 });
19 threads[i].start();
20 }
21 for (int j = 0; j < THREAD_NUM; j++) {
22 threads[j].join();
23 }
24 System.out.println("CAS costs time: " + (System.currentTimeMillis() - begin));
25 }
26
27 public void syncAdd() throws InterruptedException {
28 long begin = System.currentTimeMillis();
29 Thread[] threads = new Thread[THREAD_NUM];
30 for (int i = 0; i < THREAD_NUM; i++) {
31 threads[i] = new Thread(new Runnable() {
32 public void run() {
33 while (syncI < MAX_VALUE) {
34 synchronized ("syncI") {
35 ++syncI;
36 }
37 }
38 }
39 });
40 threads[i].start();
41 }
42 for (int j = 0; j < THREAD_NUM; j++)
43 threads[j].join();
44 System.out.println("sync costs time: " + (System.currentTimeMillis() - begin));
45 }
46 }

在我的双核cpu上运行,结果如下:

可见在不同的线程下,采用CAS计算消耗的时间远多于使用synchronized方式。原因在于第15行

14                     while (casI.get() < MAX_VALUE) {
15 casI.getAndIncrement();
16 }

的操作是一个耗时非常少的操作,15行执行完之后会立刻进入循环,继续执行,从而导致线程冲突严重。

三、改进的CAS使用场景

为了解决上述问题,只需要让每一次循环执行的时间变长,即可以大幅减少线程冲突。修改代码如下:

 1 public class CASDemo {
2 private final int THREAD_NUM = 1000;
3 private final int MAX_VALUE = 1000;
4 private AtomicInteger casI = new AtomicInteger(0);
5 private int syncI = 0;
6 private String path = "/Users/pingping/DataCenter/Books/Linux/Linux常用命令详解.txt";
7
8 public void casAdd2() throws InterruptedException {
9 long begin = System.currentTimeMillis();
10 Thread[] threads = new Thread[THREAD_NUM];
11 for (int i = 0; i < THREAD_NUM; i++) {
12 threads[i] = new Thread(new Runnable() {
13 public void run() {
14 while (casI.get() < MAX_VALUE) {
15 casI.getAndIncrement();
16 try (InputStream in = new FileInputStream(new File(path))) {
17 while (in.read() != -1);
18 } catch (IOException e) {
19 e.printStackTrace();
20 }
21 }
22 }
23 });
24 threads[i].start();
25 }
26 for (int j = 0; j < THREAD_NUM; j++)
27 threads[j].join();
28 System.out.println("CAS Random costs time: " + (System.currentTimeMillis() - begin));
29 }
30
31 public void syncAdd2() throws InterruptedException {
32 long begin = System.currentTimeMillis();
33 Thread[] threads = new Thread[THREAD_NUM];
34 for (int i = 0; i < THREAD_NUM; i++) {
35 threads[i] = new Thread(new Runnable() {
36 public void run() {
37 while (syncI < MAX_VALUE) {
38 synchronized ("syncI") {
39 ++syncI;
40 }
41 try (InputStream in = new FileInputStream(new File(path))) {
42 while (in.read() != -1);
43 } catch (IOException e) {
44 e.printStackTrace();
45 }
46 }
47 }
48 });
49 threads[i].start();
50 }
51 for (int j = 0; j < THREAD_NUM; j++)
52 threads[j].join();
53 System.out.println("sync costs time: " + (System.currentTimeMillis() - begin));
54 }
55 }

在while循环中,增加了一个读取文件内容的操作,该操作大概需要耗时40ms,从而可以减少线程冲突。测试结果如下:

可见在资源冲突比较小的情况下,采用CAS方式和synchronized同步效率差不多。为什么CAS相比synchronized没有获得更高的性能呢?

测试使用的jdk为1.7,而从jdk1.6开始,对锁的实现引入了大量的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销。而其中自旋锁的原理,类似于CAS自旋,甚至比CAS自旋更为优化。具体内容请参考 深入JVM锁机制1-synchronized。

传送门:http://blog.csdn.net/chen77716/article/details/6618779

四、总结

1、使用CAS在线程冲突严重时,会大幅降低程序性能;CAS只适合于线程冲突较少的情况使用。

2、synchronized在jdk1.6之后,已经改进优化。synchronized的底层实现主要依靠Lock-Free的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。

http://www.cnblogs.com/everSeeker/p/5569414.html

Java并发编程总结2——慎用CAS(转)的更多相关文章

  1. Java并发编程总结2——慎用CAS

    一.CAS和synchronized适用场景 1.对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源:而CAS基于硬件实 ...

  2. Java并发编程:什么是CAS?这回总算知道了

    无锁的思想 众所周知,Java中对并发控制的最常见方法就是锁,锁能保证同一时刻只能有一个线程访问临界区的资源,从而实现线程安全.然而,锁虽然有效,但采用的是一种悲观的策略.它假设每一次对临界区资源的访 ...

  3. Java并发编程系列-(3) 原子操作与CAS

    3. 原子操作与CAS 3.1 原子操作 所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何context switch,也就是切换到另一个线程. 为了实 ...

  4. JAVA并发编程: CAS和AQS

       版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u010862794/article/details/72892300 说起JAVA并发编程,就不得不聊 ...

  5. Java并发编程入门与高并发面试(三):线程安全性-原子性-CAS(CAS的ABA问题)

    摘要:本文介绍线程的安全性,原子性,java.lang.Number包下的类与CAS操作,synchronized锁,和原子性操作各方法间的对比. 线程安全性 线程安全? 线程安全性? 原子性 Ato ...

  6. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  7. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

  8. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  9. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

随机推荐

  1. SQL学习之高级联结(自联结、自然联结、外联接)

    create table Customers( Id ,), Company ) null, Name ) null ) insert into Customers values('Fun4All', ...

  2. C# 实现磁性窗体

    可以实现窗体的 吸附 移动 分离     using System; using System.Drawing; using System.Collections.Generic; using Sys ...

  3. VS2013无法链接到TFS (转)

    VS2013无法链接到TFS(Visual studio online),错误TF31001,TF31002   TF31002: Unable to connect to VisualStudio ...

  4. FSM(有限状态机)

    游戏引擎是有限状态机最为成功的应用领域之一,由于设计良好的状态机能够被用来取代部分的人工智能算法,因此游戏中的每个角色或者器件都有可能内嵌一个状态机.考虑RPG游戏中城门这样一个简单对象,它具有打开( ...

  5. php json_encode url链接出现双转义字符‘\\’和中文被编码的解决方法

    在PHP开发中 json_encode方法使用的频率还是蛮高的. 当数据中出现中文是,经过json_encode后,中文被编码了,不是显示的原中文字符. 可以通过urlencode编码后,在使用jso ...

  6. less 工具

    less 工具也是对文件或其它输出进行分页显示的工具,应该说是linux正统查看文件内容的工具,功能极其强大.less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面翻 ...

  7. javascript 修改css样式

    abc.css CSS code .class1     {    width:10px;    background-color: red;    } HTML code <!DOCTYPE ...

  8. 苹果iPhone不能判断红外发射管的好坏

    用手机来检测红外发射管好坏是目前比较常用的方法.实际操作比较简单,就是按照红外发射管的工作电压给发射管接上电源后,把手机的摄像头对着红外发射管就能看出好坏了.由于红外线是肉眼看不见的,如果不通过手机摄 ...

  9. 自己动手写RTP服务器——用RTP协议传输TS流

    上一篇文章我们介绍了关于RTP协议的知识,那么我们现在就自己写一个简单的传输TS流媒体的RTP服务器吧. 预备知识 关于TS流的格式:TS流封装的具体格式请参考文档ISO/IEC 13818-1.这里 ...

  10. 使用vi编辑binary文件

    原理:使用xxd将当前文件转成hex格式,编辑,然后再转回去 /usr/bin/xxd xxd - make a hexdump or do the reverse 例子: 用binary模式启动vi ...