目录
目标
Atomic相关类和CAS机制
原子操作
- 存在原子操作的问题。
核心在于资源在该操作中保持一致
- 反编译后字节码指令
读取- 赋值- 相加
- 分析字节码执行顺序
- 问题原因
在t1线程put后,t2线程在t1线程put之前操作,操作了无效数据,导致出现数据一致性问题。
原子操作和可见性问题的区别
可见性:先写后读,无法读到。
此原子问题:在写之前就已读取,所以和可见性问题没有关系。
CAS(Compare and swap)
由硬件来进行保证原子性,同一时间存在多个CAS操作,则只会有一个操作能成功,失败则自旋(循环)。此操作可以解决原子性问题。
在java中如何实现CAS操作
/**
*使用unsafe进行CAS操作
*/
public class CounterUnsafe {
volatile int i = 0;
private static Unsafe unsafe = null;
//i字段的偏移量
private static long valueOffset;
static {
//unsafe = Unsafe.getUnsafe();
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
//修改当前内的i字段
Field fieldi = CounterUnsafe.class.getDeclaredField("i");
//偏移量和类型无关
valueOffset = unsafe.objectFieldOffset(fieldi);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public void add() {
//i++;
for (;;){ //自旋
int current = unsafe.getIntVolatile(this, valueOffset);
//修改成功返回true 失败返回false
if (unsafe.compareAndSwapInt(this, valueOffset, current, current+1))
break;
}
}
}
public static void main(String[] args) throws InterruptedException {
final CounterUnsafe ct = new CounterUnsafe();
for (int i = 0; i < 6; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
ct.add();
}
System.out.println("done...");
}
}).start();
}
Thread.sleep(6000L);
System.out.println(ct.i);
}
J.U.C包内的原子操作封装类
原理:使用Unsafe工具类实现的原子操作。
CAS存在的问题
ABA问题
添加版本号,使用带有版本号的引用对象。
AtomicStampedReference.java
线程安全的概念
可能失效结果:使用未更新的数据或是正确的数据进行下一步处理。
出现线程安全的主要原因:
- 可见性问题;
- 原子性问题:在计算过程中某个数据失效了。
共享资源
不可变对象:只有get方法,没有set方法