转载:http://www.jb51.net/article/86192.htm

下面小编就为大家带来一篇Java并发编程总结——慎用CAS详解。小编觉得挺不错的, 现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

一、CAS和synchronized适用场景

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

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

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

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

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

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

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

三、改进的CAS使用场景

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

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

在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。

四、总结

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

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

以上这篇Java并发编程总结——慎用CAS详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

CAS适用场景的更多相关文章

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

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

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

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

  3. Java高性能编程之CAS与ABA及解决方法

    Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...

  4. CAS你知道吗?原子类AtomicInteger的ABA问题谈谈?

    (1)CAS是什么?  比较并交换 举例1,  CAS产生场景代码? import java.util.concurrent.atomic.AtomicInteger; public class CA ...

  5. 并发编程的基石——CAS机制

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 Java中提供了很多原子操作类来保证共享变量操作的原子性 ...

  6. disruptor 高并发编程 简介demo

    原文地址:http://www.cnblogs.com/qiaoyihang/p/6479994.html disruptor适用于大规模低延迟的并发场景.可用于读写操作分离.数据缓存,速度匹配(因为 ...

  7. 2019滴滴java面试总结 (包含面试题解析)

    2019滴滴java面试总结  (包含面试题) 本人6年开发经验.今年年初找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是既有php也有Java后端开发,最终选择去了滴滴 ...

  8. 2019头条java面试总结 (包含面试题解析)

    2019滴滴java面试总结  (包含面试题) 本人8年开发经验.今年年初找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.滴滴等公司offer,岗位是Java后端开发. 面试了很多家公司,感觉大部分 ...

  9. Java并发-Synchronized关键字

    一.多线程下的i++操作的并发问题 package passtra; public class SynchronizedDemo implements Runnable{ private static ...

随机推荐

  1. 从零开始学安全(三十)●使用sqlmap对网站一步步渗透

    常规注入步骤第一步注入点-u "url" 判断是否是注入点 有就判断用户的权限第二步获取数据库 所有的 -u "url" --dbs第三步 查看应用程序所有数据 ...

  2. 关于laravel5 消息订阅/发布的理解初

    laravel5.4感觉官网文档说滴不够详细...安装predis官网很详细,这里略过.... 生成命令 直接使用 Artisan 命令 make:command,该命令会在 app/Console/ ...

  3. springMVC_08文件上传

    一.步骤总结 导入jar包 配置web.xml 在src目录下创建配置文件mvc.xml 创建前段页面fileupload.jsp 创建controller类HelloController 配置mvc ...

  4. 蓝桥杯试题----- 打印大X

    打印大X 小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度.为了便于比对空格,所有的空白位置都以句点符来代替. 要求输入两个整数m n,表示笔的宽度,X的高度.用空格分开(0 ...

  5. Java io 入门

    目录 前言 代码演练 字符流 FileReader,FileWriter: BufferedReader,BufferedWriter: InputStreamReader,OutputStreamW ...

  6. angularJs - cynthia娆墨旧染-响应式文章发布系统

    (0)功能 a.添加新文章 b.修改已发布文章 c.搜索已经发布的文章 d.demo链接:   http://cynthiawupore.github.io/angularJS (1)界面 a.文章列 ...

  7. MySQL 查看执行计划

    MySQL 使用 explain + sql 语句查看 执行计划,该执行计划不一定完全正确但是可以参考. EXPLAIN SELECT * FROM user WHERE nid = 3; selec ...

  8. [HTML/CSS]导航栏的下划线跟随效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. vuejs 1.x - 实例:搜索过滤

    <!DOCTYPE html> <html> <head> <meta name="viewport" content="wid ...

  10. Android为TV端助力 帧动画

    首先在res/drawable/name1.xml/定义一组图片集合: <?xml version="1.0" encoding="utf-8"?> ...