Java-JUC(三):原子性变量与CAS算法
原子性
并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。
原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。
可见性:当多个线程同时访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
对于单线程,在执行代码时jvm会进行指令重排序,处理器为了提高效率,可以对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证保存最终执行结果和代码顺序执行的结果是一致的。
看下边的一个例子:
package com.dx.juc.test;
public class MyThread implements Runnable {
private int serialNumber = 0;
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
}
public int getSerialNumber() {
return serialNumber++;
}
}
调用:
package com.dx.juc.test;
public class Main {
public static void main(String[] args) {
MyThread thread=new MyThread();
for(int i=0;i<10;i++){
new Thread(thread).start();
}
}
}
输出结果(有时会抛出下边异常结果,但不是每次都出现异常结果):
Thread-1:0
Thread-3:2
Thread-2:1
Thread-0:0
Thread-4:3
Thread-5:4
Thread-7:5
Thread-6:6
Thread-9:7
Thread-8:8
上边这个操作错误的原因:
1)初始值serialNumer=0,thread-0把该值复制到自己的工作空间(线程私有的,该工作空间也可以叫做缓存),之后进行了三个操作:
操作一:int temp=serialNumber;// 从主存中将serialNumber复制到自己的工作空间
操作二:serialNumber=serialNumber+1;// 在自己的工作空间内进行运算
操作三:将serialNumber刷新到主存中
2)假设thread-0在操作三还未处理之前,thread-1从主存中复制serialNumber=0到自己的工作空间,然后thread-0触发操作三,而此时thread-1并不知道主存中的serialNumber已经被修改,它依然使用工作空间中的serialNum=0,也进行与thread-0一样的散步操作。
当thread-1触发操作三时(此时thread-0已经把主存中的serialNubmer修改为1)并不知道serialNumber是否被其他线程操作过,它就把在自己工作区修改的结果刷新到主存中,此时thread-1中的serialNum=1,之后的操作就是thread-1的值覆盖了thread-0的值,实际上他们值是一样的。
3)即时把int serialNumber添加上volatile修饰也不能避免该问题,从这里可以看出volatile是不具有原子性的。
在java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断,要么执行,要么不执行。
X=10; // 原子性(简单的读取、将数字赋值给变量)
Y = x; // 变量之间的相互赋值,不是原子操作
X++; // 对变量进行计算操作,此时讲过三次操作int temp=X;X=X+1;X=temp,经过获取、修改、赋值三个操作。
X = x+1;
语句2实际包括两个操作,它先要去读取x的值,再将y值写入,两个操作分开是原子性的,合在一起就不是原子性的。
语句3、4:x++ x=x+1包括3个操作:读取x的值,x+1,将x写入,所以他们也不是原子性的。
只有语句1具有原子性。
注:可以通过 synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。
使用Atomic解决原子性问题
在jdk1.5以后java.util.concurrent.atomic包下,提供了

大量的原子变量,它们内部使用CAS算法。
针对上边的代码修改为如下:
package com.dx.juc.test;
import java.util.concurrent.atomic.AtomicInteger;
public class MyThread implements Runnable {
private AtomicInteger serialNumber = new AtomicInteger();
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
}
public int getSerialNumber() {
return serialNumber.getAndIncrement();
}
}
将int serialNumber修改AtomicInteger serialNumber就具有原子性,也不在出现异常结果。
CAS算法
CAS(Compare-And-Swap)是一种硬件对并发的支持,针对多处理器操作而设计的,处理器中的一种特殊指令,用于管理对共享数据的并发访问。
CAS是一种无锁的非阻塞算法实现,是硬件对于并发操作的支持,保证了数据变量的原子性。
Cas包含了3个操作数:
- 内存值 V
- 预估值 A
- 更新值 B
当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
简单的来说,CAS有3个操作数,要读写的内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V(不做任何操作,然后重新获取主存V值,重新操作。)。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它。
CAS算法模拟:
模拟代码:
package com.dx.juc.test;
public class TestCompareAndSwap {
public static void main(String[] args) {
final CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
int expectedValue = cas.getValue();
boolean b = cas.compareAndSet(expectedValue, (int) new java.util.Random.nextInt(1000));
System.err.println(b);
}
}).start();
}
}
}
/**
* 默认CAS
**/
class CompareAndSwap {
// 内存值
private volatile int value = 0;
// 返回内存值
public synchronized int getValue() {
return value;
}
/**
* 如果预估值与原来的值一直,则修改内存为新的值,否则,不做处理。 无论是否修改,都返回原来的内存值。
**/
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
System.out.println("old:" + oldValue + ",expectedValue:" + expectedValue + ",newValue:" + newValue);
if (expectedValue == oldValue) {
value = newValue;
}
return oldValue;
}
// 如果更新成功,舊的內內存值和預估值相等。
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
输出结果:
old:0,expectedValue:0,newValue:224
trueold:224,expectedValue:224,newValue:303
old:303,expectedValue:0,newValue:690
old:303,expectedValue:0,newValue:500 true
false
false
old:303,expectedValue:303,newValue:639
true
old:639,expectedValue:0,newValue:525
false
old:639,expectedValue:303,newValue:978
false
old:639,expectedValue:639,newValue:734
trueold:734,expectedValue:734,newValue:803 old:803,expectedValue:734,newValue:455
true
false
Java-JUC(三):原子性变量与CAS算法的更多相关文章
- (一)juc线程高级特性——volatile / CAS算法 / ConcurrentHashMap
1. volatile 关键字与内存可见性 原文地址: https://www.cnblogs.com/zjfjava/category/979088.html 内存可见性(Memory Visibi ...
- 三、原子变量与CAS算法
原子变量:jdk1.5 后 java.util.concurrent.atomic 包下提供了常用的原子变量: - AtomicBoolean - AtomicInteger - AtomicLong ...
- Java多线程-----原子变量和CAS算法
原子变量 原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题 Java给我们提供了以下几种原子类型: AtomicInteger和Ato ...
- 原子变量与CAS算法
原子变量 为了引出原子变量这个概念,我们先看一个例子. package com.ccfdod.juc; public class TestAtomicDemo { public static void ...
- juc-2-原子变量与CAS算法
i++的原子性问题 例子 int i=10; i++; 结果 i=10 分析过程 在计算机 底层 会有生成一个临时变量 tem ...
- volatile关键字与内存可见性&原子变量与CAS算法
1 .volatile 关键字:当多个线程进行操作共享数据时, 可以保证内存中的数据可见 2 .原子变量:jdk1.5后java.util.concurrent.atomic 包下提供常用的原子变量 ...
- 原子变量与CAS算法小结
CAS算法 CAS(compare-and-swap)是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问. CAS是一种无锁非阻塞算法的实现. CAS ...
- 原子变量与CAS算法(二)
一.锁机制存在的问题 (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2)一个线程持有锁会导致其它所有需要此锁的线程挂起. (3)如果一个优先级高的线程等待一个 ...
- Java多线程系列——原子类的实现(CAS算法)
1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...
随机推荐
- ASP.NET Core 中的框架级依赖注入
https://tech.io/playgrounds/5040/framework-level-dependency-injection-with-asp-net-core 作者: Gunnar P ...
- .NET开源分布式日志框架ExceptionLess实战演练(公开版)
一.课程介绍 在以前,我们做日志收集大多使用 Log4net,Nlog 等框架,在应用程序变得复杂并且集群的时候,可能传统的方式已经不是很好的适用了,因为收集各个日志并且分析他们将变得麻烦而且浪费时间 ...
- Image控件Stretch属性
通过设置Image控件Stretch属性的值可以控制图片的显示形式: 包含的值:None.Fill.Uniform.UniformToFill <Grid x:Name="Layout ...
- Windows Phone本地数据库(SQLCE):7、Database mapping(翻译)
这是“windows phone mango本地数据库(sqlce)”系列短片文章的第七篇. 为了让你开始在Windows Phone Mango中使用数据库,这一系列短片文章将覆盖所有你需要知道的知 ...
- smartsvn学习(一)Xcode下svn客户端使用指南
http://smartsvn.com/features 说明 场景 执行步骤 创建新项目 一,二,三,四 下载项目 一,二,四 代码提交 五 代码更新 六 一,打开SCM 在xcode中,点击菜单: ...
- Informix存储过程
一.存储过程概述 存储过程是一个用户定义的函数,由存储过程语句(SPL) 和一组SQL语句组成,以可以执行代码形式存储在数据库中,和表.视图.索引等一样,是数据库的一种对象. 存储过程语言SPL(St ...
- java基础之static(静态)
静态的属性.方法等属于类而不是对象. 静态的方法能够由类直接调用,不须要将类实例化. 本篇主要说明:1.态的代码.成员变量要比构造方法先运行. 2. 子类的构造方法会默认去调用父类的不带參数的构造方法 ...
- 浏览器中回车(Enter)和刷新的区别是什么?[转载]
在浏览器中回车和F5刷新有什么区别那?今天就来说说:浏览器中回车(Enter)和刷新的区别是什么? 这点事. 概论: 1.回车在 Expires有效的时候,是不会去请求服务器的,打开调试看到的请求也只 ...
- cocos2d-x 保持屏幕点亮及自动变灰
很早之前遇到的问题,现在记录一下.有一家Android渠道(抱歉,时间太长了已经记不大清楚是哪一家了 oppo/联想/酷派?)在我们提交新版本时拒绝了,理由是:手机背光状态下,屏幕不会自动变灰. 这里 ...
- ExtJS 4.2 教程-02:bootstrap.js 工作方式
转载自起飞网,原文地址:http://www.qeefee.com/extjs-course-2-bootstrap-js ExtJS 4.2 教程-01:Hello ExtJS ExtJS 4.2 ...