Java原子操作保证方案
引言
原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。通常所说的原子操作包括对非long和double型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么做,但并不保证)。
三种保证方式
- 加锁
- AtomicXXX
- LongAdder
现在使用1000个线程,对一个数进行加1操作,每一个线程操作10_0000遍;
最终结果应该是:10_0000*1000 = 1_0000_0000
不作任何操作
在多线程的情况下,对一个变量进行修改,如果不做任何操作,使用传统的方式来进行的话,会导致最终的结果不是我们想要的结果。
int count = 0;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
UnsaveTest unsaveTest = new UnsaveTest();
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
int j = 0;
//每个线程对成员变量count进行加1操作,每个线程加10_0000次,1000个线程。
//所以 最终结果应该是:10_0000*1000 = 1_0000_0000
while (j++ < 10_0000) {
unsaveTest.count++;
}
});
}
long timeStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
for (int i = 0; i < 1000; i++) {
threads[i].join();
}
long timeEnd = System.currentTimeMillis();
System.out.println("期望结果:"+100000 * 1000);
System.out.println("运行结果 : " + unsaveTest.count);
System.out.println("耗时:" + (timeEnd - timeStart));
}
运行结果
期望结果:100000000
运行结果 : 97416060
耗时:82
加锁Synchronized
通过加锁的方式来保证原子操作,但有的时候同步变量只有一个,如果依然使用加锁的方式来保证原子性,在多线程竞争激烈的情况下,升级为重量级锁,会导致效率低下。
代码如下
int count = 0;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
UnsaveTest unsaveTest = new UnsaveTest();
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
int j = 0;
//每个线程对成员变量count进行加1操作,每个线程加10_0000次,1000个线程。
//所以 最终结果应该是:10_0000*1000 = 1_0000_0000
while (j++ < 10_0000) {
synchronized (unsaveTest) {
unsaveTest.count++;
}
}
});
}
long timeStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
for (int i = 0; i < 1000; i++) {
threads[i].join();
}
long timeEnd = System.currentTimeMillis();
System.out.println("期望结果:" + 100000 * 1000);
System.out.println("运行结果 : " + unsaveTest.count);
System.out.println("耗时:" + (timeEnd - timeStart));
}
运行结果
期望结果:100000000
运行结果 : 100000000
耗时:2574
使用AtomicXXXX
AtomicXXXX是并发包里面提供的一系列并发工具类,通过Unsafe的CAS操作(最终调用是通过CPU指令级别的方式)来保证原子操作,也就是所谓的无锁优化
代码如下
AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
UnsaveTest unsaveTest = new UnsaveTest();
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
int j = 0;
//每个线程对成员变量count进行加1操作,每个线程加10_0000次,1000个线程。
//所以 最终结果应该是:10_0000*1000 = 1_0000_0000
while (j++ < 10_0000) {
unsaveTest.counter.incrementAndGet();
}
});
}
long timeStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
for (int i = 0; i < 1000; i++) {
threads[i].join();
}
long timeEnd = System.currentTimeMillis();
System.out.println("期望结果:" + 100000 * 1000);
System.out.println("运行结果 : " + unsaveTest.counter.get());
System.out.println("耗时:" + (timeEnd - timeStart));
}
运行结果
期望结果:100000000
运行结果 : 100000000
耗时:1006
使用LongAdder
LongAdder是JDK1.8后为了提高AtomicLong运行效率的一个新并发工具类
代码如下:
LongAdder longAdder = new LongAdder();
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
UnsaveTest unsaveTest = new UnsaveTest();
Thread[] threads = new Thread[1000];
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(() -> {
int j = 0;
//每个线程对成员变量count进行加1操作,每个线程加10_0000次,1000个线程。
//所以 最终结果应该是:10_0000*1000 = 1_0000_0000
while (j++ < 10_0000) {
unsaveTest.longAdder.increment();
}
});
}
long timeStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
for (int i = 0; i < 1000; i++) {
threads[i].join();
}
long timeEnd = System.currentTimeMillis();
System.out.println("期望结果:" + 100000 * 1000);
System.out.println("运行结果 : " + unsaveTest.longAdder.intValue());
System.out.println("耗时:" + (timeEnd - timeStart));
}
运行结果
期望结果:100000000
运行结果 : 100000000
耗时:212
总结
通过以上的几种测试,我们可以得出以下结果
在不作任何其它操作的情况下,运行效率最高,但是结果却不正确,这也是最大的问题。如果一个操作不能保证结果正确,那它就失去了意义。
在保证结果正确的情况下
Synchronized效率最低
Synchronized在多线程竞争激烈的情况下,效率会显著下降,因为无锁————》偏向锁————》轻量级锁(自旋锁)————》重量级锁,大量线程在多次等待锁的过程中,会自旋为重量级锁,这个时候效率会变得很低。
其次是AtomicInteger
AtomicInteger底层使用的Unsafe类的CAS操作,是通过CPU指令级别的方式来保证原子操作。效率也还可以。
效率最高的是LongAdder。
LongAdder类与AtomicLong类的区别在于高并发时前者将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;而在并发较低时仅对base变量进行CAS操作,与AtomicLong类原理相同
Java原子操作保证方案的更多相关文章
- Java架构师方案—多数据源开发详解及原理(二)(附完整项目代码)
1. mybatis下数据源开发工作 2. 数据源与DAO的关系原理模型 3. 为什么要配置SqlSessionTemplate类的bean 4. 多数据源应用测试 1. mybatis下数据源开发工 ...
- Java 并发系列之九:java 原子操作类Atomic(13个)
1. 原子更新基本类型类 2. 原子更新数组 3. 原子更新引用 4. 原子更新属性 5. txt java 原子操作类Atomic 概述 java.util.concurrent.atomic里的原 ...
- 项目四:Java秒杀系统方案优化-高性能高并发实战
技术栈 前端:Thymeleaf.Bootstrap.JQuery 后端:SpringBoot.JSR303.MyBatis 中间件:RabbitMQ.Redis.Druid 功能模块 分布式会话,商 ...
- Java如何保证文件落盘?
本文转载自Java如何保证文件落盘? 导语 在之前的文章Linux/UNIX编程如何保证文件落盘中,我们聊了从应用到操作系统,我们要如何保证文件落盘,来确保掉电等故障不会导致数据丢失.JDK也封装了对 ...
- java原子操作
一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中 ...
- java原子操作的实现原理--转载
原文地址:http://www.infoq.com/cn/articles/atomic-operation 1. 引言 原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic ...
- Java原子操作类AtomicInteger应用场景
Java中有那么一些类,是以Atomic开头的.这一系列的类我们称之为原子操作类.以最简单的类AtomicInteger为例.它相当于一个int变量,我们执行Int的 i++ 的时候并不是一个原子操作 ...
- Java原子操作类汇总
当程序更新一个变量时,如果是多线程同时更新这个变量,可能得到的结果与期望值不同.比如:有一个变量i,A线程执行i+1,B线程也执行i+1,经过两个线程的操作后,变量i的值可能不是期望的3,而是2.这是 ...
- Java原子操作类,你知道多少?
原子操作类简介 由于synchronized是采用的是悲观锁策略,并不是特别高效的一种解决方案. 实际上,在J.U.C下的atomic包提供了一系列的操作简单,性能高效,并能保证线程安全的类去 更新基 ...
- 面试必备:Java 原子操作的实现原理[精品长文]
本文整理自<Java并发编程的艺术>第二章 作者:方腾飞 魏鹏 程晓明 原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被 ...
随机推荐
- 专访香侬科技:致力于让世界听到中文NLP的声音
像所有的创业者一样,香侬科技的初创团队胸怀梦想,期待有一天当人们提起香侬的时候,除了"信息论之父",还能想起来有一家用技术在链接大千世界的科技公司--香侬科技. 新生的香侬科技选 ...
- 技术干货 | mPaaS 小程序高玩带你起飞:客户端预置小程序无视网络质量
简介: 弱网拉包无障碍,深度提升用户体验 传统的小程序技术容易受到网络环境影响,当网络质量不佳时可能导致拉取不到小程序包的情况.通过预置小程序,即可规避该问题.本文介绍了预置小程序的原理和预置小程序的 ...
- 每次都需要解释大量指令?使用 PolarDB-X 向量化引擎
简介: 向量化引擎为PolarDB-X的表达式计算带来了显著的性能提升. 介绍 PolarDB-X是阿里巴巴自研的云原生分布式数据库,采用了计算-存储分离的架构,其中计算节点承担着大量的表达式计算任务 ...
- 面向B端算法实时业务支撑的工程实践
简介:在营销场景下,算法同学会对广告主提供个性化的营销工具,帮助广告主更好的精细化营销,在可控成本内实现更好的ROI提升.我们在这一段时间支持了多个实时业务场景,比如出价策略的实时化预估.关键词批量服 ...
- 网关流控利器:结合 AHAS 实现 Ingress/Nginx 流量控制
简介:微服务的稳定性一直是开发者非常关注的话题.随着业务从单体架构向分布式架构演进以及部署方式的变化,服务之间的依赖关系变得越来越复杂,业务系统也面临着巨大的高可用挑战. 作者:涂鸦 微服务的稳定性 ...
- MaxCompute跨境访问加速解决方案
简介: MaxCompute联合全球加速服务,为有跨境访问需求的MaxCompute客户提供一套高效稳定的跨境访问加速方案. MaxCompute联合全球加速服务,为有跨境访问需求的MaxComput ...
- DataWorks 功能实践速览03期 — 生产开发环境隔离
简介: DataWorks功能实践系列,帮助您解析业务实现过程中的痛点,提高业务功能使用效率! 往期回顾: DataWorks 功能实践速览01期--数据同步解决方案:为您介绍不同场景下可选的数据同 ...
- [GPT] 神经网络模型方面的课程、神经网络模型与深度学习
现在有很多关于神经网络模型的课程.以下是一些比较受欢迎的神经网络模型课程: Stanford CS231n:卷积神经网络(CNNs)课程 Deep Learning Specialization: ...
- vue项目上线前优化(路由懒加载的使用,外部CDN的使用)
引 当使用vue做完项目后,接下来当然是要进行线上部署了.但是在上线之前还是可以做很多方面优化的,可以让项目上线后的体验更加哦. 若是使用了vue-cli的话,可以从面板界面直观的看到各项数据,控制台 ...
- 13.prometheus监控tengine(无用)
一.环境准备 1.1 docker安装tengine带nginx-module-vts模块(二选一) mkdir /data/ -p cd /data/ # 通过git clone下载已经创建好的do ...