Java原子类与Unsafe
最近了解到CAS(Compare And Swap)算法,比较并交换,意思是当旧值和内存当前值匹配的时候,执行交换,以完成原子操作。现代CPU指令集都支持CAS操作,因此CAS经常用于完成无阻塞算法。
那么问题来了,Java中如何确保原子操作呢?如果使用Java本身的语法,就是加锁了。下面以Counter类为例。
package org.lin.demo.java8;
public class Counter {
private int value;
//加锁保证原子性
public synchronized void set(int newValue) {
value = newValue;
}
//模拟CAS
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
if (value == expectedValue) {
value = newValue;
return true;
}
return false;
}
}
但是,如果使用加锁的方式完成CAS,就没有意义了。因此,Java原子类中大量使用了Unsafe类。
AtomicInteger源码片段如下:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 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;
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Unsafe类,正如其名,是一个不安全的类。它是用JNI实现的,可以直接操作内存的一个类。可以看到AtomicInteger使用Unsafe完成原子操作,避免使用锁。
如何获取Unsafe实例呢?
1.使用Unsafe的静态工厂方法,Unsafe.getUnsafe(),会抛出安全异常。
jdk执行了权限检查以保证不被信赖的类无法使用Unsafe.
2.使用反射获取Unsafe实例
Unsafe部分源码如下:
/**
* This class should provide access to low-level operations and its
* use should be limited to trusted code. Fields can be accessed using
* memory addresses, with undefined behaviour occurring if invalid memory
* addresses are given.
* 这个类提供了一个更底层的操作并且应该在受信任的代码中使用。可以通过内存地址
* 存取fields,如果给出的内存地址是无效的那么会有一个不确定的运行表现。
*
* @author Tom Tromey ([email protected])
* @author Andrew John Hughes ([email protected])
*/
public class Unsafe {
// Singleton class.
private static Unsafe unsafe = new Unsafe();
/**
* Private default constructor to prevent creation of an arbitrary
* number of instances.
* 使用私有默认构造器防止创建多个实例
*/
private Unsafe() {
}
/**
* Retrieve the singleton instance of <code>Unsafe</code>. The calling
* method should guard this instance from untrusted code, as it provides
* access to low-level operations such as direct memory access.
* 获取<code>Unsafe</code>的单例,这个方法调用应该防止在不可信的代码中实例,
* 因为unsafe类提供了一个低级别的操作,例如直接内存存取。
*
* @throws SecurityException if a security manager exists and prevents
* access to the system properties.
* 如果安全管理器不存在或者禁止访问系统属性
*/
public static Unsafe getUnsafe() {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPropertiesAccess();
return unsafe;
} }
可以看到Unsafe类有一个static的字段,通过反射,我们可以绕开限制,获得Unsafe实例,例如:
package org.lin.demo.java8;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class MyUnsafe {
int num;
public static void main(String[] args) throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe)theUnsafe.get(null);
MyUnsafe my = new MyUnsafe();
long offset = unsafe.objectFieldOffset(MyUnsafe.class.getDeclaredField("num"));
System.out.println("unsafe = " + offset);
unsafe.getAndSetInt(my, offset, 1);
printNum(my);
unsafe.compareAndSwapInt(my, offset, 0, 2);
printNum(my);
unsafe.compareAndSwapInt(my, offset, 1, 5);
printNum(my);
}
private static void printNum(MyUnsafe my) {
System.out.println("num = " + my.num);
}
}
由于Unsafe的很多操作,都是直接操作内存,所以获取要操作的字段在内存中的偏移量,是使用Unsafe的第一步。Unsafe的其他使用方法可以参考API文档(到网上去找,jdk隐藏了源码)。