volatile类型变量操作

volatile类型的实例变量的修改,JDK提供了J.U.C下的atomic包下类来支持。

1
2
3
1、AtomicInteger/AtomicLong/AtomicBoolean/AtomicReference是关于对变量的原子更新,
2、AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray是关于对数组的原子更新
3、AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdater<T,V>是基于反射的原子更新字段的值。

这里也有一些约束,只能是非final的实例变量,并且被volatile修饰。

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Mock {
private volatile String[] name = new String[]{};
private volatile int index = 0;

private static AtomicIntegerFieldUpdater indexUpdater =
AtomicIntegerFieldUpdater.newUpdater(Mock.class, "index");

private static final AtomicReferenceFieldUpdater<Mock, String[]> nameUpdater =
AtomicReferenceFieldUpdater.newUpdater(Mock.class, String[].class, "name");

public int getAndIncrementIndex() {
return indexUpdater.getAndIncrement(this);
}

public String[] getName() {
nameUpdater.set(this, new String[]{"zero"});
return nameUpdater.get(this);
}
}

AtomicXxxFieldUpdater本质上通过sun.misc.Unsafe来完成操作,在java.util.concurrent.locks包中可以发现很多Unsafe的使用痕迹,例如AbstractQueuedSynchronizer。

Unsafe提供了static方法来获取Unsafe的类变量theUnsafe,即一个Unsafe对象:

1
2
3
4
5
6
7
8
9
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}

但是,可以发现,只有调用类的ClassLoader是Bootstrp ClassLoader能返回这个theUnsafe,否则会抛出SecurityException异常。因为这个类如其名,unsafe,该类设计初衷也不想被应用程序使用,只能被rt.jar中的类使用。但是实际上是可以通过反射机制拿到这个值的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UnsafeFactory {
private static final Unsafe UNSAFE;

static {
Unsafe unsafe;
try {
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(null);
} catch (Throwable t) {
System.out.println("sun.misc.Unsafe.theUnsafe: unavailable, {}.");

unsafe = null;
}
UNSAFE = unsafe;
}

public static Unsafe getUnsafe() {
return UNSAFE;
}

private UnsafeFactory() {
throw new RuntimeException("Cann't Access UnsafeFactory Constructor");
}
}

然后就可以用Unsafe来完成对volatile变量的修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Mock {
private volatile int index = 0;

private static final Unsafe unsafe = UnsafeFactory.getUnsafe();
private static final long indexOffset;

static {
try {
indexOffset = unsafe.objectFieldOffset(Mock.class.getDeclaredField("index"));
} catch (Exception ex) {
throw new Error(ex);
}
}

public int getAndAddIndex(int i) {
return unsafe.getAndAddInt(this, indexOffset, i);
}
}