1谈谈 volatile 理解
volatile: 低配的同步锁,保障有序性(禁止指令重排,内存屏障),可见性(打小报告)
有序性是如何保证的:通过插入内存屏障,来禁止 屏障 之前与屏障之后的指令交换位置
可见性:
2谈谈CAS
含义,底层原理,Unsafe 类理解
CAS 缺点:
多线程环境下,对共享变量的操作,要么加锁,要么CAS
神马是CAS?
CAS 底层实现?
CAS 优缺点?
CAS ABA 问题?
如何解决?
原子引用?
CAS 含义以及的层代码实现(AtomicInteger 类)
加锁 :保证只能同时有一个线程去操作 数据
CAS:比较交换,偏量值 ,主要思想是通过读取主内存的值 和预期旧值比较,如果相同,则将新值=预期旧值+偏量值 写入主内存(如果写入失败,重新读取旧值)
经典的使用例子便是jdk 中的atomic 包(查看源码可以看见有乐观锁的思想)
以AtomicInteger 的 getAndIncrement() 方法为例
AtomicInteger atomicInteger=new AtomicInteger();
atomicInteger.getAndIncrement();
--》
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 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
*/
// O 是指当前对象,或者说是首地址
//offset 是指内存偏移量
、// delta 是指变化量
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
// 读取当前对象+offset 内存地址上的值
v = getIntVolatile(o, offset);// 获取主内存值
} while (!compareAndSwapInt(o, offset, v, v + delta));// 如果没有交换成功,重新读取主内存值,进行下一次交换,直到成功为止
return v;
}
compareAndSwapInt 的底层就是 c语言的实现了,位于unsafe.cpp 中
UNSAFE_ENTRY(jboolean,uNSAFE_cOMPAREaNDsWAPiNT(jnieNV *env,jobject unsafe,jlong offset,iint e,jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p=JNIHandles::resolve(obj);// 对象首地址
jint * addr=(jint*) index_oop_from_field_offset_long(p,offset);// 对象的属性字段的地址
return (jint)(Atomic::cmpxchg(x,addr,e))==e;// 注意atomic 则保证此操作是原子性的不可中断的
UNSAFE_END
CAS=Compare and Swap CPU 并发原语(原子性)
CAS 优缺点
- 如果CAS 失败,会一直尝试CAS,空转,CPU开销
- 只能保证单个变量
- ABA 问题
ABA 问题
主内存:初始值 1
线程 A 和线程B 读取主内存 值 为自己工作空间(拷贝一份)
线程 在CAS 过程中 将数据从主内存获取(得到1)
线程A 将数据改为 改为2 并且完成刷回主内存
线程A 将数据改为1 并且完成刷回主内存
线程B 执行 CompareAndSwap 操作 将数据 更改为3(CAS 个会成功)
因为线程B 读取到的仍是1(虽然不是最初的那个1,中途被改变,线程B无法感知)
忽略了中间过程的变化,只注意了首尾状态
原子引用更新
Atomic 只是提供了 基本数据类型的原子操作
如果现在有User/Account 这些类型也需要原子操作呢?
class User{
String name;
int age;
public User(String name, int age