Java下CAS(Compare And Swap)并发操作测试

原创 2016年08月30日 16:51:36

测试目的

据我的了解, 在高并发环境下, 为了减少锁的开销(睡眠, 线程上下文切换), 采用的是无锁编程(lock-free or lockless programming), 而无锁编程的基础是CAS操作, 那么CAS操作在高并发下的效果怎样, 怎么尽量避免并发带来的问题.

测试的Java代码:

package com.lqp.test;

import java.util.concurrent.atomic.AtomicLong;

public class ConcurrentCASTest {

    public static void main(String[] args) throws Exception {
        final AtomicLong value = new AtomicLong();

        final int count = 100 * 10000;
        int threadCount = 1;

        for (int i = 0; i < threadCount; i++) {
            final int id = i;
            new Thread(new Runnable() {

                @Override
                public void run() {
                    long start = System.nanoTime();

                    int failCount = 0;
                    for (int i = 0; i < count; i++) {
                        long initVal = value.get();

                        boolean suc = value.compareAndSet(initVal, initVal + 1);

                        if (!suc) {
                            failCount++;
                        }
                    }

                    long diffMilis = (System.nanoTime() - start) / 1000 / 1000;

                    println("Time = " + diffMilis + " milis" + ", ThreadId = " + id + ", fail count = " + failCount + ", failPercent = " + (failCount * 100) / (double)count);
                }
            }).start();
        }
    }

    public static boolean println(String msg) {
        System.out.println(msg);

        return true;
    }
}

说明:

  • 循环100W次对一个AtomicLong变量, 取值, +1, 然后CAS设置值, 失败则统计失败次数, 结束时记录100W次操作所用的时间
  • threadCount是线程数量, 改变线程数目观察失败情况
  • 虽然测试语言是java, 但底层核心是仍然cpu操作指令 ‘lock cmpxchg’, 不用C测试是因为写起来不太方便
  • CPU配置Intel(R) Core(TM) i7-4710MQ CPU @ 2.50GHz, 8核
  • java版本信息
    • java version “1.8.0_91”
    • Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
    • Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

测试结果:

threadCount = 1, 即单线程下无并发修改操作, 测试3次:

  1. Time = 16 milis, ThreadId = 0, fail count = 0, failPercent = 0.0
  2. Time = 16 milis, ThreadId = 0, fail count = 0, failPercent = 0.0
  3. Time = 16 milis, ThreadId = 0, fail count = 0, failPercent = 0.0

threadCount = 2, 双线程同时修改, 测试3次:

  1. Time = 62 milis, ThreadId = 1, fail count = 347064, failPercent = 34.7064
    Time = 64 milis, ThreadId = 0, fail count = 463725, failPercent = 46.3725
  2. Time = 65 milis, ThreadId = 1, fail count = 478589, failPercent = 47.8589
    Time = 66 milis, ThreadId = 0, fail count = 477393, failPercent = 47.7393
  3. Time = 66 milis, ThreadId = 0, fail count = 489875, failPercent = 48.9875
    Time = 65 milis, ThreadId = 1, fail count = 490575, failPercent = 49.0575

threadCount = 4, 4线程同时修改, 测试2次:

  1. Time = 150 milis, ThreadId = 0, fail count = 663234, failPercent = 66.3234
    Time = 152 milis, ThreadId = 1, fail count = 747265, failPercent = 74.7265
    Time = 154 milis, ThreadId = 3, fail count = 666898, failPercent = 66.6898
    Time = 154 milis, ThreadId = 2, fail count = 675097, failPercent = 67.5097
  2. Time = 134 milis, ThreadId = 1, fail count = 522787, failPercent = 52.2787
    Time = 148 milis, ThreadId = 3, fail count = 746457, failPercent = 74.6457
    Time = 150 milis, ThreadId = 2, fail count = 744108, failPercent = 74.4108
    Time = 150 milis, ThreadId = 0, fail count = 710502, failPercent = 71.0502

threadCount = 4, 4线程同时修改, 测试1次:

Time = 258 milis, ThreadId = 2, fail count = 753286, failPercent = 75.3286
Time = 269 milis, ThreadId = 6, fail count = 769007, failPercent = 76.9007
Time = 277 milis, ThreadId = 5, fail count = 750360, failPercent = 75.036
Time = 281 milis, ThreadId = 3, fail count = 799387, failPercent = 79.9387
Time = 283 milis, ThreadId = 0, fail count = 790358, failPercent = 79.0358
Time = 285 milis, ThreadId = 1, fail count = 765177, failPercent = 76.5177
Time = 279 milis, ThreadId = 7, fail count = 763133, failPercent = 76.3133
Time = 285 milis, ThreadId = 4, fail count = 791819, failPercent = 79.1819

可以看到, 在两个线程竞争的情况下, 失败率就已经到达近50%了, 这也好理解, 一人一半的概率成功. 对于高并发下来说, 性能比没有竞争下下降了4倍左右. 4线程竞争的情况下, 下降10倍左右; 8线程下, 下降接近20倍. 在实际情况中, 失败的时候很可能选择继续尝试, 直至成功, 除了时间上的进一步增加, 由于失败而进一步尝试也导致了CPU周期的浪费.

什么具体场景下可能会遇到上述的情况呢? 在我的认知领域里, JVM在分配内存的时候就有可能遇到. 我们知道heap是线程共同访问的, java里面分配对象是很常见的操作, 当线程很多, 分配的时候就很可能产生竞争. 尽管hotspot里的分配可以由一条CAS的操作搞定, 但竞争激烈情况下, 仍然会发生性能退化.

怎么去减少这种不良效应呢? 一个方法就是采用线程本地化, 让操作尽量不去参加竞争. 比如在hotspot中, 每个线程会一次性从heap中申请一块稍大的内存TLAB(thread local allocation buffer), 然后对象分配时, 优先从这块thread local的内存分配, 由于是线程私有的, 因此不需要CAS的操作即可完成分配, 只有当TLAB中不能满足时, 才会使用CAS的方式分配. 在其他需要减少竞争的地方, 也可用借鉴这种思路来解决.

Compare And Swap(CAS)实现无锁多生产者

struct node{ struct node *next; int data; }struct node *queue;//队列头多个消费者(多线程)都需要向这个queue插入数据 为了说明...
  • xy010902100449
  • xy010902100449
  • 2015年07月27日 21:59
  • 1500

JAVA CAS原理深度分析

看了一堆文章,终于把JAVA CAS的原理深入分析清楚了。 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到!   参考文档: http://www.blogjava.net...
  • Hsuxu
  • Hsuxu
  • 2013年07月25日 13:07
  • 116083

java并发中的原子变量和原子操作以及CAS介绍

起源于一道面试题i++是不是原子操作?什么事原子操作?于是乎搜集答案:       很多情况下我们只是需要一个简单的、高效的、线程安全的递增递减方案。注意,这里有三个条件:简单,意味着程序员尽可能...
  • wxw520zdh
  • wxw520zdh
  • 2016年12月18日 23:11
  • 1579

Java并发编程--CAS(Compare And Swap)无锁算法

转发: http://www.cnblogs.com/Mainz/p/3546347.html锁的代价锁是用来做并发控制的,代价也是非常高的。当我们内核进行一次加锁操作时操作系统进行了一次上下文的切换...
  • jlj417
  • jlj417
  • 2017年07月16日 14:37
  • 76

CAS原子操作实现无锁及性能分析

原文地址:http://blog.csdn.net/chen19870707/article/details/41083183 CAS原子操作实现无锁及性能分析   Author...
  • u011244446
  • u011244446
  • 2016年09月14日 15:24
  • 884

JAVA高并发 无锁(CAS)

 Java当中提供了一些有关无锁类的使用,在底部使用比较交换指令来实现。一般来说有锁的方式,会导致线程可能会阻塞、挂起,在进入临界区之前由系统对它进行阻塞和挂起,相对来讲无锁的性能会更好些,除非...
  • zhang15238156629
  • zhang15238156629
  • 2017年04月15日 22:08
  • 211

Java中CAS详解

在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。 ...
  • ls5718
  • ls5718
  • 2016年09月17日 17:23
  • 17124

java高并发:CAS无锁原理及广泛应用

前言在现在的互联网技术领域,用户流量越来越大,系统中并发量越来越大,大公司的日活动辄成百上千万。如何面对如此高的并发是当今互联网技术圈一直在努力的事情。 应对高并发需要在各个技术层面进行合理的设计和...
  • fgyibupi
  • fgyibupi
  • 2016年12月20日 12:53
  • 5606

CAS-比较&交换并发处理策略

这是一种可以称为基于冲突检测的乐观锁。这种模式下,已经没有所谓的锁概念了,每条线程都直接先去执行操作,计算完成后检测是否与其他线程存在共享数据竞争,如果没有则让此操作成功,如果存在共享数据竞争则可能不...
  • fyxxq
  • fyxxq
  • 2016年08月25日 17:42
  • 1281

CAS :compare and swap非阻塞同步算法

CAS(乐观锁算法)compare and swap,解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原...
  • qq_17612199
  • qq_17612199
  • 2016年04月20日 15:32
  • 240
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java下CAS(Compare And Swap)并发操作测试
举报原因:
原因补充:

(最多只允许输入30个字)