Implémenter la classe AtomicInteger avec Unsafe
Principe
AtomicInteger peut réaliser des mises à jour atomiques dans des conditions simultanées, évitant l'utilisation de synchronized, et les performances sont très élevées.
La couche inférieure d'AtomicInteger est implémentée par Unsafe. Ici, nous écrivons manuellement une classe AtomicInteger et la testons.
Exemple de test
Démarrez 1000 threads et chaque thread effectue une opération de -10 yuans. Si le solde initial est de 10 000, le résultat correct doit être 0.
Le code suivant a réussi le test dans JDK1.8.
Classe de compte
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");
}
}
Classe d'implémentation de compte
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);
}
}
Classe 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;
}
}
tester
public class Test {
public static void main(String[] args) {
Account account = new DiyAccount(10000);
Account.demo(account);
}
}
Production
0 cost: 65 ms
Classe d'outils Other-Unsafe UnsafeAccessor
Unsafe
La méthode de construction que nous connaissons est privée et il n'y a aucun get
moyen d'obtenir l'objet, nous ne pouvons donc instancier l' Unsafe
objet que par réflexion .
public final class Unsafe {
private static final Unsafe theUnsafe;
private Unsafe() {
}
...
}
et donc
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;
}
}
Code de projet