我们认为,由于思维定式原子变量总是比同步运行的速度更快,我想是这样也已经,直到实现了ID在第一次测试过程生成器不具有在这样一个迷迷糊糊的东西。

测试代码:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class ConcurrentAdder {private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);private static int I = 0;private static final Object o = new Object();private static volatile long start;public static void main(final String[] args) {//每一个线程运行多少次累加int round = 10000000;//线程个个数int threadn = 20;start = System.currentTimeMillis();atomicAdder(threadn, round);//syncAdder(threadn, round);}static void atomicAdder(int threadn, int addTimes) {int stop = threadn * addTimes;List<Thread> list = new ArrayList<Thread>();for (int i = 0; i < threadn; i++) {list.add(startAtomic(addTimes, stop));}for (Thread each : list) {each.start();}}static Thread startAtomic(final int addTimes, final int stop) {Thread ret = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < addTimes; i++) {int v = ATOMIC_INTEGER.incrementAndGet();if (stop == v) {System.out.println("value:" + v);System.out.println("elapsed(ms):" + (System.currentTimeMillis() - start));System.exit(1);}}}});ret.setDaemon(false);return ret;}static void syncAdder(int threadn, int addTimes) {int stop = threadn * addTimes;List<Thread> list = new ArrayList<Thread>();for (int i = 0; i < threadn; i++) {list.add(startSync(addTimes, stop));}for (Thread each : list) {each.start();}}static Thread startSync(final int addTimes, final int stop) {Thread ret = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < addTimes; i++) {synchronized (o) {I++;if (stop == I) {System.out.println("value:" + I);System.out.println("elapsed(ms):" + (System.currentTimeMillis() - start));System.exit(1);}}}}});ret.setDaemon(false);return ret;}
}

这是一个非常easy的累加器,N个线程并发累加,每一个线程累加R次。

分别凝视

atomicAdder(threadn, round);//原子变量累加
syncAdder(threadn, round);//同步累加

中的一行运行还有一行

笔者机器的配置:i5-2520M 2.5G 四核

N=20

R=10000000

结果:

原子累加:15344 ms

同步累加:10647 ms

问题出来了,为什么同步累加会比原子累加要快50%左右?

@ 我们知道java加锁的过程是(内置sync和显式lock类似),要加锁的线程检查下锁是否被占用。假设被占用则增加到目标锁的等待队列。假设没有则。加锁。

这里我们每一个线程获取到锁累加之后就立刻又去获取锁,这时其它线程还没有被唤醒。锁又被当前线程拿到了。

这也就是非公平锁可能造成的饥饿问题。

可是这一个原因不能解释50%的性能提升?理论上。在一个绝对时间。总有一个线程累加成功,那么两种累加器的耗时应该近似才对。

那么是有什么提升了同步累加的性能。或者是什么减少了原子累加的性能?

@接下来笔者分别perf了一下两种累加器的运行过程:

第一次运行的是原子累加器,第二次运行的同步累加器。

wxf@pc:/data$ perf stat -e cs -e L1-dcache-load-misses java ConcurrentAdder
value:100000000
elapsed(ms):8580Performance counter stats for 'java ConcurrentAdder 1 100 1000000':21,841 cs                                                          233,140,754 L1-dcache-load-misses                                       8.633037253 seconds time elapsed

wxf@pc:/data$ perf stat -e cs -e L1-dcache-load-misses java ConcurrentAdder
value:100000000
elapsed(ms):5749Performance counter stats for 'java ConcurrentAdder 2 100 1000000':55,522 cs                                                          28,160,673 L1-dcache-load-misses                                       5.811499179 seconds time elapsed

我们能够看出,同步累加的上下文切换是要比原子累加多。这个能够理解,加锁本身就会添加线程的切换。

再看,原子累加器的L1缓存失效比同步累加器高一个数量级

笔者茅塞顿开,原子操作会导致缓存一致性问题。从而导致频繁的缓存行失效。缓存一致性协议MESI见:http://en.wikipedia.org/wiki/MESI_protocol

可是这时同步累加器在一个CPU周期内重复的获取锁操作。缓存并没有失效。

再把每次累加的线程ID输出来,会发现。原子累加的线程分布要分散非常多。

回到问题上来。为什么我们会一直觉得原子操作比加锁要快呢?文中的样例是非常特别非常特别的,正常业务场景下,我们累加过后,要经过非常多业务代码逻辑才会再次去累加,这里已经跨过非常多个CPU时间片了。从而同步累加器非常难一直获取到锁。这中情况下,同步累加器即会有等待加锁的性能损失还会有缓存一致性带来的性能损失。

所以在一般的情况下,同步累加器会慢非常多。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

转载于:https://www.cnblogs.com/hrhguanli/p/4740290.html

谈论Java原子变量和同步的效率 -- 颠覆你的生活相关推荐

  1. Java 原子变量类

          今天看了下 Java中的同步机制,刚刚开始看,内容比较浅,其中有一个被称为原子变量类的东西感觉还是很有意思的,所以,记录一下吧.       首先,我们先看一段这样的代码: public ...

  2. java原子变量的作用_AtomicInteger原子类的作用介绍(代码示例)

    本篇文章给大家带来的内容是关于AtomicInteger原子类的作用介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. AtomicInteger 原子类的作用 多线程操 ...

  3. java原子变量的作用_原子变量 - jock_javaEE - 博客园

    一.原子变量的作用:解决并发下多个线程操作共享数据存在,线程安全问题 原子变量 = volatile + CAS算法[又叫无锁机制] 二. 例子 package com.atguigu.juc; im ...

  4. Java:多线程(同步死锁、锁原子变量、线程通信、线程池)

    5,同步和死锁 5.1,线程同步 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象: 修饰一个方法,被修饰的方法称为同步方法,其作用 ...

  5. JAVA 并发编程实践 - 原子变量与非阻塞同步机制 笔记

    2019独角兽企业重金招聘Python工程师标准>>> 非阻塞算法: 利用底层的源自机器指令(比如CAS)代替锁来实现数据在并发访问中的一致性.应用于:操作系统和JVM中实现线程/进 ...

  6. 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

    这篇说说java.util.concurrent.atomic包里的类,总共12个,网上有很多文章解析这几个类,这里挑些重点说说. 这12个类可以分为三组: 1. 普通类型的原子变量 2. 数组类型的 ...

  7. Java多线程(二)之Atomic:原子变量与原子类

    一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中 ...

  8. Java™ 教程(原子变量)

    原子变量 java.util.concurrent.atomic包定义了支持单个变量的原子操作的类,所有类都有get和set方法,类似于对volatile变量的读写操作,也就是说,set与在同一个变量 ...

  9. java cas原理_Java并发之原子变量及CAS算法-上篇

    Java并发之原子变量及CAS算法-上篇 编辑 ​ 概述 本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK提供的类中是怎么保证变量原子性的呢?.对应Java中的包是:java.uti ...

最新文章

  1. 【图像处理opencv】_图像边缘
  2. rust和gta5哪个吃配置_选指数基金,像“今晚吃什么”一样容易
  3. 四、华为鸿蒙HarmonyOS应用开发之Java开发下Page Ability生命周期
  4. C++11 并发指南六(atomic 类型详解三 std::atomic (续))
  5. 运算器为计算机提供了计算与逻辑,【单选题】运算器为计算机提供了计算与逻辑功能,因此称它为()....
  6. fedora 20 报错: Loaded plugins: langpacks, refresh-packagekit Error: Cannot retrieve metalink for...
  7. 【bzoj 4455】小星星(树型DP+容斥原理+dfs建树和计算的2种方式)
  8. Linux中Docker部署Nginx
  9. dictionary new一个实例_超全的Python 字典(Dictionary)介绍
  10. 通用JdbcDao更新,增加动态数据源,支持权重和读写分离
  11. 解决 SQL 注入的另类方法
  12. 商业云平台和开源云平台
  13. arcgis使用教程和视频教程
  14. 精简版Android ProtoBuf入门
  15. 彻底卸载微软拼音输入法
  16. 江苏卫视舞蹈演员机器人_百度机器人踢馆江苏卫视《芝麻开门》
  17. android 显示大屏幕_android android如何将优化的体验带到大屏幕
  18. 从零开始的MySQL数据库三部曲(二、MySQL数据库的创库创表增删改查篇)
  19. Django创建app应用和admin模块
  20. 王者荣耀产品分析(本人不是产品,只是自己分析了一下)

热门文章

  1. android 嵌套分组拖动_GitHub - Mosect/DragLayout: Android拖拽控件,支持上下左右滑动、折叠或者嵌套ListView、RecyclerView等...
  2. python结束进程树_【python爬虫】线程进程
  3. linux下oracle 9204 soft only,linux 下oracle 9i的安装
  4. 【算法竞赛学习】二手车交易价格预测-Task5模型融合
  5. 『操作系统』微内核结构的操作系统几何?(优缺点)
  6. 数学--数论--快速乘法+快速幂
  7. 十分钟看懂什么是VoLTE【包学包会,否则退款】
  8. Face-landmarks-detection-benchmark 人脸特征定位网站汇总
  9. 银行利率涨了,定期存款有必要取出再存吗?
  10. 华为2017年财报,为何6036亿销售收入,净利润才479亿?