Atomic类和CAS
说Atomic类之前,先聊一聊volatile。
对volatile的第一印象就是可见性。所谓可见性,就是一个线程对共享变量的修改,别的线程能够感知到。
但是对于原子性,volatile是不能保证的。来看看自增操作的问题:
volatile int i; i++;
i++ 在多线程环境下,是不能保证最终的结果正确的。比如某个时刻,i=5,线程A读取了i的值,说时迟那时快,就在马上要执行++操作时,线程A突然就被切换走了;然后线程B也读取i的值,进行了++操作。这时i的值是6,即使线程A的工作内存中的缓存已经失效,线程A已经读取了i的值为5,不会再去读取,所以++操作后,i的值还是6。
关于volatile的底层实现,有好多文章分析的很透彻,这里不再赘述。
那么除了使用synchronized,还有没有其它的方式来解决上述问题呢?天空一声巨响,Atomic类闪亮登场!!!
先看看AtomicInteger类,其它类型包装成的Atomic类,请读者自习。
AtomicInteger i = new AtomicInteger(0); i.incrementAndGet();
用法相当简单,incrementAndGet 方法能实现原子性的自增操作。如何实现,看源码。
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
Unsafe类:
/**
* Atomically adds the given value to the current value of a field
* or array element within the given object <code>o</code>
* at the given <code>offset</code>.
*
* @param o object/array to update the field/element in
* @param offset field/element offset
* @param delta the value to add
* @return the previous value
* @since 1.8
*/
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
可以看到原子性的实现没有用synchronized,说明是非阻塞同步。最核心的方法是compareAndSwapInt,也就是所谓的CAS操作。
CAS操作依赖底层硬件的CAS指令,CAS指令有两个步骤:冲突检测和更新操作,但是这两个步骤合起来成为一个原子性操作。
CAS指令需要3个操作数:内存位置(V),旧的预期值(A),新值(B)。CAS指令执行时,首先比较内存位置V处的值和A的值是否相等(冲突检测),如果相等,就用新值B覆盖A(更新操作),否则,就什么也不做。所以,一般循环执行CAS操作,直到成功为止。
Unsafe类里面的compareAndSwapXXX 方法最后都会变成与硬件相关的CAS指令。从Unsafe这个类名就可以看出,作者不希望我们随便使用,因为是不安全的。为什么不安全呢,因为这个类可以直接操作内存;还有其他的一些底层操作,比如上篇文章提到的将线程挂起,就是调用了Unsafe类的park方法(感兴趣的,出门左拐)。
了解了CAS和Unsafe类,接着再说AtomicInteger类:
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset; static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value;
value就是我们需要操作的真正int值,unsafe就是Unsafe类的单例,valueOffset在static语句块里面,被设置成了value变量在AtomicInteger类的实例对象里面的偏移量(可以看成内存地址)。这里对对象的内存布局如有疑问,同样出门一路左拐,找到那篇将Oop和Klass的文章。
也就是说,Atomic类通过循环进行CAS操作,直到成功,来实现非阻塞同步,进而变成原子操作。
在java.util.concurrent.atomic包下还有一些看上去比较奇怪的类,XXXFieldUpdater类,这玩意儿是用来干什么的呢?
一句话概括,就是亡羊补牢。比如说,你先自己写了一个类,定义了一个基础类型的变量。后来涉及到多线程,那么原来对该变量的一些操作就变得不安全。如果,你立马想到的是手动修改代码,那就太low了,破坏了设计模式里面比较重要的开闭原则,而且很多情况下,你是接触不到别人的源代码的。这个时候,XXXFieldUpdater类就派上用场了。这个时候,猜也能猜到,这个类肯定是通过反射的方式实现这个功能的。另外,有一点原来定义的继承类型变量,必须是volatile的。
直接上代码,看效果:
public class Test{
public volatile int a = 100;
// 多线程下不安全
public void incr() {
a++;
}
}
public class SafeTest{
private static AtomicIntegerFieldUpdater<Test> update = AtomicIntegerFieldUpdater.newUpdater(Test.class, "a");
private static Test test = new Test();
// 多线程下安全
public void incr() {
update.incrementAndGet(test);
}
}
update.incrementAndGet(test)无非也是调用了Unsafe类的CAS操作,核心方法是AtomicIntegerFieldUpdater.newUpdater(Test.class, "a"),看看源码:
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
} AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
final String fieldName,
final Class<?> caller) {
// 反射逻辑
final Field field;
final int modifiers;
try {
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) &&
((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
} Class<?> fieldt = field.getType();
if (fieldt != int.class)
throw new IllegalArgumentException("Must be integer type");
// 必须是volatile
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type"); this.cclass = (Modifier.isProtected(modifiers) &&
caller != tclass) ? caller : null;
this.tclass = tclass;
// 得到变量的偏移量,相当于内存地址
offset = unsafe.objectFieldOffset(field);
}
总的来说,原子性的实现是依赖于Unsafe类的CAS操作,直接修改内存里的值,既危险又刺激。我们甚至可以用Unsafe类来直接分配内存,要不试一试!!!
Unsafe类是单例,可以通过它的getUnsafe方法获取这个单例。
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
// 调用方的类的类加载器必须是启动类加载器
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
这个方法对调用方有限制,就是说你随随便便定义的类,调用这个方法是会报错的。但是Unsafe类既然已经被加载,我们可以通过反射的方式去获取里面的单例对象。
package test; import java.lang.reflect.Field; import sun.misc.Unsafe;
import sun.reflect.Reflection; class User {
private String name = "";
private int age = 0; public User() {
this.name = "test";
this.age = 25;
} @Override
public String toString() {
return name + ": " + age;
}
} public class Test {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
// 通过反射得到theUnsafe对应的Field对象
Field field = Unsafe.class.getDeclaredField("theUnsafe");
// 设置该Field为可访问
field.setAccessible(true);
// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的
Unsafe unsafe = (Unsafe) field.get(null); // 直接分配相应大小的内存,不执行构造方法
User user = (User) unsafe.allocateInstance(User.class);
System.out.println(user); User userFromNormal = new User();
System.out.println(userFromNormal); }
}
这个例子演示了Unsafe直接为某个类的实例分配内存,注意是只分配内存,没有顺便调用构造方法。
运行结果:
null: 0
test: 25
当然,Unsafe类里面还有好多的底层操作,宝藏后面慢慢挖掘。就到这里吧!!!
Atomic类和CAS的更多相关文章
- 推荐使用concurrent包中的Atomic类
这是一个真实案例,曾经惹出硕大风波,故事的起因却很简单,就是需要实现一个简单的计数器,每次取值然后加1,于是就有了下面这段代码: private int counter = ...
- Java中Atomic类的使用分析
1:为什么会出现Atomic类 在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 稍有经验的同学都知道这种写法是线程不安全的.为了达到线程安全的目的,我们通常会用synchro ...
- 随意看看AtomicInteger类和CAS
最近在读jdk源码,怎么说呢?感觉收获还行,比看框架源码舒服多了,一些以前就感觉很模糊的概念和一些类的用法也清楚了好多,举个很简单的例子,我在读Integer类的时候,发现了原来这个类自带缓存,看看如 ...
- java中的Atomic类
文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...
- 并发编程从零开始(十一)-Atomic类
并发编程从零开始(十一)-Atomic类 7 Atomic类 7.1 AtomicInteger和AtomicLong 如下面代码所示,对于一个整数的加减操作,要保证线程安全,需要加锁,也就是加syn ...
- Java多线程系列九——Atomic类
参考资料:https://fangjian0423.github.io/2016/03/16/java-AtomicInteger-analysis/http://www.cnblogs.com/54 ...
- java.util.concurrent.atomic 类包详解
java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurren ...
- CAS机制总结
一.简介 CAS机制:(Compare and set)比较和替换 简单来说–>使用一个期望值来和当前变量的值进行比较,如果当前的变量值与我们期望的值相等,就用一个新的值来更新当前变量的值CAS ...
- 无锁同步-JAVA之Volatile、Atomic和CAS
1.概要 本文是无锁同步系列文章的第二篇,主要探讨JAVA中的原子操作,以及如何进行无锁同步. 关于JAVA中的原子操作,我们很容易想到的是Volatile变量.java.util.concurren ...
随机推荐
- 谈一款MOBA游戏《码神联盟》的服务端架构设计与实现
一.前言 <码神联盟>是一款为技术人做的开源情怀游戏,每一种编程语言都是一位英雄.客户端和服务端均使用C#开发,客户端使用Unity3D引擎,数据库使用MySQL.这个MOBA类游戏是笔者 ...
- (转)Java.lang.reflect.Method invoke方法 实例
背景:今天在项目中用到Method 的invoke方法,但是并不理解,查完才知道,原来如此! import java.lang.reflect.Method; /** * Java.lang.refl ...
- (转)FastJson---高性能JSON开发包
场景:javaBean对象转化为json对象! 1 Fastjson介绍 Fastjson是一个Java语言编写的JSON处理器,由阿里巴巴公司开发.1.遵循http://json.org标准,为其官 ...
- markdown 常用格式API
摘要 记录常用格式 参考:https://www.zybuluo.com/mdeditor 1. 标题 写法: 文字前加 #, 几个# 表示几级标题 标题下方增加 = 或 - 效果 标题1 标题2 标 ...
- 【JAVASCRIPT】React学习-JSX 语法
摘要 react 学习包括几个部分: 文本渲染 JSX 语法 组件化思想 数据流 JSX 语法 1. 定义 JSX 是javascript + xml 的合集,我们可以将javascript 与 ht ...
- Asp.Net MVC-4-过滤器1:认证与授权
基础 过滤器体现了MVC框架中的Aop思想,虽然这种实现并不完美但在实际的开发过程中一般也足以满足需求了. 过滤器分类 依据上篇分析的执行时机的不同可以把过滤器按照实现不同的接口分为下面五类: IAu ...
- 智慧航空AI大赛-阿里云算法大赛总结 第一赛季总结
[以前的文章]最后一公里极速配送 - 阿里云算法大赛总结 总结一下新的教训 1.由于都是NP难题,获得最优解用常规的方法非常困难,对于不是算法科班出身的人来说,首先应该到网络上寻找一下论文,是否有一些 ...
- 如何使用lxml的XPath功能
用python写爬虫呢,最常见的过滤方式有BeautifulSoup, css selector, XPath, 如果使用前两个,BeautifulSoup包就能解决,然而使用XPath,就要引入lx ...
- S2-032代码执行
Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架.其全新的Struts 2的体系结构与Struts 1的体系结构差别 ...
- 成为Java顶尖程序员 ,看这11本书就够了(转)
学习的最好途径就是看书",这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书的 ...