一、原子操作
Java中可以通过锁和循环CAS的方式来实现原子操作。JVM中的CAS操作正是利用了上文中提到的处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止,具体的类可以参见juc下的atomic包内的原子类。
原子操作是针对CPU来说的,是一个不可能再分割的一个操作,要么不执行,要么执行完毕。CAS的锁就是操作系统的缓存行锁或者总线锁。
多个CPU对主内存同一块缓存行进行CAS操作时,会遵循操作系统的缓存行一致性协议MESI,基于处理器提供的CMPXCHG指令,CPU会把变量读取到CPU寄存器中进行比较与交换操作(也就是CAS操作),优先操作完的CPU会把缓存行锁住并修改缓存行为修改M状态,其它CPU变为无效I。操作失败的CPU会等待操作成功的CPU把值写回主存内,这也是为什么CAS要通过循环来实现原子操作。
注意:一个原子操作在Java中是通过CAS操作实现,那么一系列的原子操作是怎么实现的?通过同步块加锁就可以实现。
二、Atomic
在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
- 基本类:AtomicInteger、AtomicLong、AtomicBoolean;
- 引用类型:AtomicReference、AtomicReference的ABA实例、AtomicStampedRerence、AtomicMarkableReference;
- 数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;
- 属性原子修改器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
应用
创建相应的操作类型,传入初始值。
public static void main(String[] args) throws InterruptedException {
AtomicInteger atomicInteger = new AtomicInteger(1);
for(int i = 0; i<100; i++)
{
new Thread(new Runnable() {
@Override
public void run() {
atomicInteger.incrementAndGet();
}
}).start();
}
Thread.sleep(1000);
System.out.println("原子操作结果---->"+atomicInteger.get());
}
原子操作ABA问题
多线程同时操作同一个AtomicInteger时,比如T1 T2同时操作AtomicInteger atomic时,atomic初始值为2,T1 先读取atomic,值为2,这是T2进来,多次修改了atomic,先 2-->1,再 1-->2,T2结束,T1继续往下,把atomic修改了,而且修改成功了。这就是ABA问题,问题主要出现在T2先执行,期间T1修改了atomic而T2却不知道。
JUC包中AtomicReference可以解决AtomicInteger ABA的问题,原理就是引进了版本这一概念,atomic每修改一次,版本就变一次。这样,T1修改了atomic后T2能感知的到。
private static AtomicStampedReference<Integer> atomicStampedRef =
new AtomicStampedReference<>(1, 0);
public static void main(String[] args){
Thread main = new Thread(() -> {
int stamp = atomicStampedRef.getStamp(); //获取当前标识别
System.out.println("操作线程" + Thread.currentThread()+ "stamp="+stamp + ",初始值 a = " + atomicStampedRef.getReference());
try {
Thread.sleep(1000); //等待1秒 ,以便让干扰线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean isCASSuccess = atomicStampedRef.compareAndSet(1,2,stamp,stamp +1); //此时expectedReference未发生改变,但是stamp已经被修改了,所以CAS失败
System.out.println("操作线程" + Thread.currentThread() + "stamp="+stamp + ",CAS操作结果: " + isCASSuccess);
},"主操作线程");
Thread other = new Thread(() -> {
int stamp = atomicStampedRef.getStamp();
atomicStampedRef.compareAndSet(1,2,stamp,stamp+1);
System.out.println("操作线程" + Thread.currentThread() + "stamp="+atomicStampedRef.getStamp() +",【increment】 ,值 = "+ atomicStampedRef.getReference());
stamp = atomicStampedRef.getStamp();
atomicStampedRef.compareAndSet(2,1,stamp,stamp+1);
System.out.println("操作线程" + Thread.currentThread() + "stamp="+atomicStampedRef.getStamp() +",【decrement】 ,值 = "+ atomicStampedRef.getReference());
},"干扰线程");
main.start();
other.start();
}
三、Unsafe魔术类
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

Unsafe类为一单例实现,提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。
如何获取Unsafe实例?
1、从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。
2、通过反射获取单例对象theUnsafe。
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeInstance {
public static Unsafe reflectGetUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Unsafe对象中使用CAS
public class AtomicStudentAgeUpdater {
private String name ;
private volatile int age;
private static final Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset(AtomicStudentAgeUpdater.class.getDeclaredField("age"));
} catch (Exception e) {
throw new Error(e);
}
}
public void compareAndSwapAge(int old,int target){
unsafe.compareAndSwapInt(this,valueOffset,old,target);
}
public AtomicStudentAgeUpdater(String name,int age){
this.name = name;
this.age = age;
}
public int getAge(){
return this.age;
}
public static void main(String[] args) {
AtomicStudentAgeUpdater updater = new AtomicStudentAgeUpdater("张三",18);
updater.compareAndSwapAge(18,17);
}
}
Unsafe线程调度
static Object object = new Object();
Unsafe unsafe = UnsafeInstance.reflectGetUnsafe();
//加锁解锁
unsafe.monitorEnter(object);
//同步块
unsafe.monitorExit(object);
//线程阻塞、唤醒
unsafe.park();
unsafe.unpark();
Unsafe内存屏障,防止指令重排
UnsafeInstance.reflectGetUnsafe().loadFence();//读屏障
UnsafeInstance.reflectGetUnsafe().storeFence();//写屏障
UnsafeInstance.reflectGetUnsafe().fullFence();//读写屏障
网友评论