Implementar la clase AtomicInteger con Unsafe
Principio
AtomicInteger puede lograr actualizaciones atómicas en condiciones concurrentes, evitando el uso de sincronizados, y el rendimiento es muy alto.
Unsafe implementa la capa inferior de AtomicInteger. Aquí escribimos a mano una clase AtomicInteger y la probamos.
Ejemplo de prueba
Inicie 1000 subprocesos y cada subproceso realiza una operación de -10 yuanes. Si el saldo inicial es 10000, el resultado correcto debería ser 0.
El siguiente código pasó la prueba en JDK1.8.
Clase de cuenta
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做 -10 元 的操作
* 如果初始余额为 10000 那么正确的结果应当是 0
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
long start = System.nanoTime();
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(account.getBalance()
+ " cost: " + (end-start)/1000_000 + " ms");
}
}
Clase de implementación de cuenta
class DiyAccount implements Account {
private DiyAtomicInteger balance;
public DiyAccount(int balance) {
this.balance = new DiyAtomicInteger(balance);
}
@Override
public Integer getBalance() {
return balance.getValue();
}
@Override
public void withdraw(Integer amount) {
balance.decrement(amount);
}
}
Clase DiyAtomicInteger
class DiyAtomicInteger {
private volatile int value; //实例操作的变量
private static final long valueOffset;//偏移量
private static final Unsafe UNSAFE;// unsafe实例对象
static {
UNSAFE = UnsafeAccessor.getUnsafe();
try {
valueOffset = UNSAFE.objectFieldOffset(DiyAtomicInteger.class.getDeclaredField("value"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public int getValue() {
return value;
}
public void decrement(int amount) {
while (true) {
int prev = this.value;
int next = prev - amount;
if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {
break;
}
}
}
public DiyAtomicInteger(int value) {
this.value = value;
}
}
prueba
public class Test {
public static void main(String[] args) {
Account account = new DiyAccount(10000);
Account.demo(account);
}
}
Salida
0 cost: 65 ms
Otra clase de herramienta no segura UnsafeAccessor
Unsafe
El método de construcción que conocemos es privado y no hay get
forma de obtener el objeto, por lo que solo podemos instanciar el Unsafe
objeto a través de la reflexión .
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}
...
}
y entonces
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeAccessor {
private static final Unsafe unsafe;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
public static Unsafe getUnsafe() {
return unsafe;
}
}
Código de proyecto