一、CAS
1.保護共享資源
加鎖方式:
public class TestAccount {
public static void main(String[] args) {
Account account = new AccountSafe(10000);
Account.demo(account);
}
}
interface Account {
// 獲取余額
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法內(nèi)會啟動 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");
}
}
class AccountSafe implements Account {
private Integer balance;
public AccountSafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
synchronized (this) {
return this.balance;
}
}
@Override
public void withdraw(Integer amount) {
synchronized (this) {
this.balance -= amount;
}
}
}
0 cost: 96 ms
無鎖方式:
public class TestAccount {
public static void main(String[] args) {
Account account = new AccountSafe(10000);
Account.demo(account);
}
}
interface Account {
// 獲取余額
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法內(nèi)會啟動 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");
}
}
class AccountCas implements Account {
private AtomicInteger balance;
public AccountCas(int balance) {
this.balance = new AtomicInteger(balance);
}
@Override
public Integer getBalance() {
return balance.get();
}
@Override
public void withdraw(Integer amount) {
while(true) {
// 獲取余額的最新值
int prev = balance.get();
// 要修改的余額
int next = prev - amount;
// 真正修改
if(balance.compareAndSet(prev, next)) {
break;
}
}
}
}
0 cost: 59 ms
2.CAS工作方式
compareAndSet:在 set 前,先比較 prev 與當前值不一致了知给,next 作廢,返回 false 表示失敗描姚,一致涩赢,以 next 設置為新值,返回 true 表示成功轩勘。
比如筒扒,別的線程已經(jīng)做了減法,當前值已經(jīng)被減成了 990
那么本線程的這次 990 就作廢了绊寻,進入 while 下次循環(huán)重試:
如果一致花墩,以 next 設置為新值悬秉,返回 true 表示成功。
CAS 的底層是 lock cmpxchg 指令(X86 架構)冰蘑,在單核 CPU 和多核 CPU 下都能夠保證【比較-交換】的原子性和泌。
在多核狀態(tài)下,某個核執(zhí)行到帶 lock 的指令時祠肥,CPU 會讓總線鎖住武氓,當這個核把此指令執(zhí)行完畢,再開啟總線仇箱。這個過程中不會被線程的調(diào)度機制所打斷县恕,保證了多個線程對內(nèi)存操作的準確性,是原子的工碾。
3.CAS底層分析
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;
private volatile int value;
CAS操作中的value使用了volatile來修飾弱睦,保證讀取到的是共享變量的最新值。
無鎖效率高:
- 無鎖情況下渊额,即使重試失敗况木,線程始終在高速運行,沒有停歇旬迹,而 synchronized 會讓線程在沒有獲得鎖的時候火惊,發(fā)生上下文切換,進入阻塞奔垦。再次喚醒代價比較大
- 無鎖情況下屹耐,因為線程要保持運行,需要額外 CPU 的支持椿猎,雖然不會進入阻塞惶岭,但由于沒有分到時間片,仍然會進入可運行狀態(tài)犯眠,還是會導致上下文切換
CAS 的特點:
結合 CAS 和 volatile 可以實現(xiàn)無鎖并發(fā)按灶,適用于線程數(shù)少、多核 CPU 的場景下筐咧。
- CAS 是基于樂觀鎖的思想:最樂觀的估計鸯旁,不怕別的線程來修改共享變量,就算改了也沒關系量蕊,再重試铺罢。
- synchronized 是基于悲觀鎖的思想:最悲觀的估計,得防著其它線程來修改共享變量残炮,上了鎖其他線程都別想改韭赘,改完了解開鎖,其他線程才有機會
- CAS 體現(xiàn)的是無鎖并發(fā)势就、無阻塞并發(fā):
因為沒有使用 synchronized泉瞻,所以線程不會陷入阻塞楷怒,這是效率提升的因素之一
但如果競爭激烈,可以想到重試必然頻繁發(fā)生瓦灶,反而效率會受影響
二、原子類
1.原子整數(shù)
AtomicInteger
AtomicInteger i = new AtomicInteger(0);
// 獲取并自增(i = 0, 結果 i = 1, 返回 0)抱完,類似于 i++
System.out.println(i.getAndIncrement());
// 自增并獲仍籼铡(i = 1, 結果 i = 2, 返回 2),類似于 ++i
System.out.println(i.incrementAndGet());
// 自減并獲惹捎椤(i = 2, 結果 i = 1, 返回 1)碉怔,類似于 --i
System.out.println(i.decrementAndGet());
// 獲取并自減(i = 1, 結果 i = 0, 返回 1),類似于 i--
System.out.println(i.getAndDecrement());
// 獲取并加值(i = 0, 結果 i = 5, 返回 0)
System.out.println(i.getAndAdd(5));
// 加值并獲冉怼(i = 5, 結果 i = 0, 返回 0)
System.out.println(i.addAndGet(-5));
// 獲取并更新(i = 0, p 為 i 的當前值, 結果 i = -2, 返回 0)
// 其中函數(shù)中的操作能保證原子撮胧,但函數(shù)需要無副作用
System.out.println(i.getAndUpdate(p -> p - 2));
// 更新并獲取(i = -2, p 為 i 的當前值, 結果 i = 0, 返回 0)
// 其中函數(shù)中的操作能保證原子老翘,但函數(shù)需要無副作用
System.out.println(i.updateAndGet(p -> p + 2));
// 獲取并計算(i = 0, p 為 i 的當前值, x 為參數(shù)1, 結果 i = 10, 返回 0)
// 其中函數(shù)中的操作能保證原子芹啥,但函數(shù)需要無副作用
// getAndUpdate 如果在 lambda 中引用了外部的局部變量,要保證該局部變量是 final 的
// getAndAccumulate 可以通過 參數(shù)1 來引用外部的局部變量铺峭,但因為其不在 lambda 中因此不必是 final
System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));
// 計算并獲饶够场(i = 10, p 為 i 的當前值, x 為參數(shù)1, 結果 i = 0, 返回 0)
// 其中函數(shù)中的操作能保證原子,但函數(shù)需要無副作用
System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));
優(yōu)化CAS:
優(yōu)化前:
public void withdraw(Integer amount) {
while(true) {
// 獲取余額的最新值
int prev = balance.get();
// 要修改的余額
int next = prev - amount;
// 真正修改
if(balance.compareAndSet(prev, next)) {
break;
}
}
}
優(yōu)化后:
public void withdraw(Integer amount) {
balance.getAndAdd(-1 * amount);
}
2.原子引用
@Slf4j
public class TestDecimal {
public static void main(String[] args) {
DecimalAccount.demo(new DecimalAccountCas(new BigDecimal("10000")));
}
}
class DecimalAccountCas implements DecimalAccount {
private AtomicReference<BigDecimal> balance;
public DecimalAccountCas(BigDecimal balance) {
// this.balance = balance;
this.balance = new AtomicReference<>(balance);
}
@Override
public BigDecimal getBalance() {
return balance.get();
}
@Override
public void withdraw(BigDecimal amount) {
while(true) {
BigDecimal prev = balance.get();
BigDecimal next = prev.subtract(amount);
if (balance.compareAndSet(prev, next)) {
break;
}
}
}
}
interface DecimalAccount {
// 獲取余額
BigDecimal getBalance();
// 取款
void withdraw(BigDecimal amount);
/**
* 方法內(nèi)會啟動 1000 個線程卫键,每個線程做 -10 元 的操作
* 如果初始余額為 10000 那么正確的結果應當是 0
*/
static void demo(DecimalAccount account) {
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(BigDecimal.TEN);
}));
}
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(account.getBalance());
}
}
ABA問題及解決
@Slf4j
public class ABA {
static AtomicReference<String> ref = new AtomicReference<>("A");
public static void main(String[] args) throws InterruptedException {
log.debug("主線程 start...");
// 獲取值 A
// 這個共享變量被其它線程修改過
String prev = ref.get();
other();
Thread.sleep(100);
// 嘗試改為 C
log.debug("change A->C {}", ref.compareAndSet(prev, "C"));
}
private static void other() throws InterruptedException {
new Thread(() -> {
log.debug("change A->B {}", ref.compareAndSet(ref.get(), "B"));
}, "t1").start();
Thread.sleep(500);
new Thread(() -> {
log.debug("change B->A {}", ref.compareAndSet(ref.get(), "A"));
}, "t2").start();
}
}
16:08:15.255 [main] DEBUG juc.automic.ABA - 主線程 start...
16:08:15.318 [t1] DEBUG juc.automic.ABA - change A->B true
16:08:15.822 [t2] DEBUG juc.automic.ABA - change B->A true
16:08:15.925 [main] DEBUG juc.automic.ABA - change A->C true
主線程僅能判斷出共享變量的值與最初值 A 是否相同傀履,不能感知到這種從 A 改為 B 又 改回 A 的情況,如果主線程希望:
只要有其它線程動過了共享變量莉炉,那么自己的 cas 就算失敗钓账,這時,僅比較值是不夠的絮宁,需要再加一個版本號:
AtomicStampedReference
@Slf4j
public class TestABA {
static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
public static void main(String[] args) throws InterruptedException {
log.debug("main start...");
// 獲取值 A
String prev = ref.getReference();
// 獲取版本號
int stamp = ref.getStamp();
log.debug("版本 {}", stamp);
// 如果中間有其它線程干擾梆暮,發(fā)生了 ABA 現(xiàn)象
other();
Thread.sleep(1000);
// 嘗試改為 C
log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1));
}
private static void other() throws InterruptedException {
new Thread(() -> {
log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B", ref.getStamp(), ref.getStamp() + 1));
log.debug("更新版本為 {}", ref.getStamp());
}, "t1").start();
Thread.sleep(500);
new Thread(() -> {
log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A", ref.getStamp(), ref.getStamp() + 1));
log.debug("更新版本為 {}", ref.getStamp());
}, "t2").start();
}
}
18:22:38.273 [main] DEBUG juc.automic.TestABA - main start...
18:22:38.275 [main] DEBUG juc.automic.TestABA - 版本 0
18:22:38.325 [t1] DEBUG juc.automic.TestABA - change A->B true
18:22:38.325 [t1] DEBUG juc.automic.TestABA - 更新版本為 1
18:22:38.826 [t2] DEBUG juc.automic.TestABA - change B->A true
18:22:38.826 [t2] DEBUG juc.automic.TestABA - 更新版本為 2
18:22:39.829 [main] DEBUG juc.automic.TestABA - change A->C false
AtomicMarkableReference
AtomicStampedReference 可以給原子引用加上版本號,追蹤原子引用整個的變化過程羞福,如: A -> B -> A ->C 惕蹄,通過AtomicStampedReference,可以知道治专,引用變量中途被更改了幾次卖陵。
但是有時候,并不關心引用變量更改了幾次张峰,只是單純的關心是否更改過泪蔫,可以使用AtomicMarkableReference
public class TestMarked {
static AtomicMarkableReference<String> atomicStampedReference = new AtomicMarkableReference("hcx",false);
public static void main(String[] args) {
boolean oldMarked = atomicStampedReference.isMarked();
String oldReference = atomicStampedReference.getReference();
System.out.println("初始化之后的標記:"+oldMarked);
System.out.println("初始化之后的值:"+oldReference);
String newReference = "hcx1";
boolean b =atomicStampedReference.compareAndSet(oldReference,newReference,true,false);
if(!b){
System.out.println("Mark不一致,無法修改Reference的值");
}
b =atomicStampedReference.compareAndSet(oldReference,newReference,false,true);
if(b){
System.out.println("Mark一致喘批,修改reference的值為hcx1");
}
System.out.println("修改成功之后的Mark:"+atomicStampedReference.isMarked());
System.out.println("修改成功之后的值:"+atomicStampedReference.getReference());
}
}
3.原子數(shù)組
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
package juc.automic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class AtomicArray {
public static void main(String[] args) {
demo(
()->new int[10],
(array)->array.length,
(array, index) -> array[index]++,
array-> System.out.println(Arrays.toString(array))
);
demo(
()-> new AtomicIntegerArray(10),
AtomicIntegerArray::length,
AtomicIntegerArray::getAndIncrement,
System.out::println
);
}
/**
參數(shù)1撩荣,提供數(shù)組铣揉、可以是線程不安全數(shù)組或線程安全數(shù)組
參數(shù)2,獲取數(shù)組長度的方法
參數(shù)3餐曹,自增方法逛拱,回傳 array, index
參數(shù)4,打印數(shù)組的方法
*/
// supplier 提供者 無中生有 ()->結果
// function 函數(shù) 一個參數(shù)一個結果 (參數(shù))->結果 , BiFunction (參數(shù)1,參數(shù)2)->結果
// consumer 消費者 一個參數(shù)沒結果 (參數(shù))->void, BiConsumer (參數(shù)1,參數(shù)2)->
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T, Integer> lengthFun,
BiConsumer<T, Integer> putConsumer,
Consumer<T> printConsumer ) {
List<Thread> ts = new ArrayList<>();
T array = arraySupplier.get();
int length = lengthFun.apply(array);
for (int i = 0; i < length; i++) {
// 每個線程對數(shù)組作 10000 次操作
ts.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j%length);
}
}));
}
// 啟動所有線程
ts.forEach(t -> t.start());
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 等所有線程結束
printConsumer.accept(array);
}
}
[8398, 8472, 8460, 8456, 8435, 8408, 8352, 8344, 8305, 8318]
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]
4.原子更新器
- AtomicReferenceFieldUpdater // 域 字段
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
利用字段更新器台猴,可以針對對象的某個域(Field)進行原子操作朽合,只能配合 volatile 修飾的字段使用,否則會出現(xiàn)異常
@Slf4j
public class Test {
public static void main(String[] args) {
Student stu = new Student();
AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
System.out.println(updater.compareAndSet(stu, null, "hcx"));
System.out.println(stu);
}
}
class Student {
volatile String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
5.原子累加器
package juc.automic;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class TestAdder {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
demo(
() -> new AtomicLong(0),
AtomicLong::getAndIncrement
);
}
for (int i = 0; i < 5; i++) {
demo(
LongAdder::new,
LongAdder::increment
);
}
}
/*
() -> 結果 提供累加器對象
(參數(shù)) -> 執(zhí)行累加操作
*/
private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
T adder = adderSupplier.get();
List<Thread> ts = new ArrayList<>();
// 4 個線程饱狂,每人累加 50 萬
for (int i = 0; i < 4; i++) {
ts.add(new Thread(() -> {
for (int j = 0; j < 500000; j++) {
action.accept(adder);
}
}));
}
long start = System.nanoTime();
ts.forEach(t -> t.start());
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(adder + " cost:" + (end - start) / 1000_000);
}
}
2000000 cost:40
2000000 cost:36
2000000 cost:33
2000000 cost:33
2000000 cost:29
2000000 cost:12
2000000 cost:5
2000000 cost:5
2000000 cost:5
2000000 cost:5
性能提升的原因:在有競爭時曹步,設置多個累加單元,Therad-0 累加 Cell[0]休讳,而 Thread-1 累加Cell[1]... 最后將結果匯總讲婚。這樣它們在累加時操作的不同的 Cell 變量,因此減少了 CAS 重試失敗俊柔,從而提高性能筹麸。
三、LongAdder
LongAdder 類有幾個關鍵域
// 累加單元數(shù)組, 懶惰初始化
transient volatile Cell[] cells;
// 基礎值, 如果沒有競爭, 則用 cas 累加這個域
transient volatile long base;
// 在 cells 創(chuàng)建或擴容時, 置為 1, 表示加鎖
transient volatile int cellsBusy;
使用cas實現(xiàn)一把鎖:
@Slf4j
public class CASLock {
// 0 沒加鎖
// 1 加鎖
private AtomicInteger state = new AtomicInteger(0);
public void lock() {
while (true) {
if (state.compareAndSet(0, 1)) {
break;
}
}
}
public void unlock() {
log.debug("unlock...");
state.set(0);
}
public static void main(String[] args) {
CASLock lock = new CASLock();
new Thread(() -> {
log.debug("begin...");
lock.lock();
try {
log.debug("lock...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
log.debug("begin...");
lock.lock();
try {
log.debug("lock...");
} finally {
lock.unlock();
}
}).start();
}
}
10:20:40.732 [Thread-0] DEBUG juc.automic.CASLock - begin...
10:20:40.732 [Thread-1] DEBUG juc.automic.CASLock - begin...
10:20:40.735 [Thread-0] DEBUG juc.automic.CASLock - lock...
10:20:41.740 [Thread-0] DEBUG juc.automic.CASLock - unlock...
10:20:41.740 [Thread-1] DEBUG juc.automic.CASLock - lock...
10:20:41.740 [Thread-1] DEBUG juc.automic.CASLock - unlock...
偽共享
Cell 即為累加單元
// 防止緩存行偽共享
@sun.misc.Contended
static final class Cell {
volatile long value;
Cell(long x) { value = x; }
// 最重要的方法, 用來 cas 方式進行累加, prev 表示舊值, next 表示新值
final boolean cas(long prev, long next) {
return UNSAFE.compareAndSwapLong(this, valueOffset, prev, next);
}
...
}
從 cpu 到 | 大約需要的時鐘周期 |
---|---|
寄存器 | 1 cycle (4GHz 的 CPU 約為0.25ns) |
L1 | 3~4 cycle |
L2 | 10~20 cycle |
L3 | 40~45 cycle |
內(nèi)存 | 120~240 cycle |
因為 CPU 與 內(nèi)存的速度差異很大婆咸,需要靠預讀數(shù)據(jù)至緩存來提升效率竹捉。
而緩存以緩存行為單位,每個緩存行對應著一塊內(nèi)存尚骄,一般是 64 byte(8 個 long)块差,緩存的加入會造成數(shù)據(jù)副本的產(chǎn)生,即同一份數(shù)據(jù)會緩存在不同核心的緩存行中倔丈。
CPU 要保證數(shù)據(jù)的一致性憨闰,如果某個 CPU 核心更改了數(shù)據(jù),其它 CPU 核心對應的整個緩存行必須失效:
因為 Cell 是數(shù)組形式需五,在內(nèi)存中是連續(xù)存儲的鹉动,一個 Cell 為 24 字節(jié)(16 字節(jié)的對象頭和 8 字節(jié)的 value),因此緩存行可以存下 2 個的 Cell 對象宏邮。
問題:
- Core-0 要修改 Cell[0]
- Core-1 要修改 Cell[1]
無論誰修改成功泽示,都會導致對方 Core 的緩存行失效,比如 Core-0 中Cell[0]=6000, Cell[1]=8000 要累加Cell[0]=6001, Cell[1]=8000 蜜氨,這時會讓 Core-1 的緩存行失效械筛。
@sun.misc.Contended
用來解決這個問題,它的原理是在使用此注解的對象或字段的前后各增加 128 字節(jié)大小的padding飒炎,從而讓 CPU 將對象預讀至緩存時占用不同的緩存行埋哟,這樣,不會造成對方緩存行的失效.
四郎汪、unsafe
Unsafe 對象提供了非常底層的赤赊,操作內(nèi)存闯狱、線程的方法,Unsafe 對象不能直接調(diào)用抛计,只能通過反射獲得
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;
}
}
Unsafe CAS 操作:
public class TestUnsafe {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe);
// 1. 獲取域的偏移地址
long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));
long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));
Teacher t = new Teacher();
// 2. 執(zhí)行 cas 操作
unsafe.compareAndSwapInt(t, idOffset, 0, 1);
unsafe.compareAndSwapObject(t, nameOffset, null, "hcx");
// 3. 驗證
System.out.println(t);
}
}
@Data
class Teacher {
volatile int id;
volatile String name;
}
使用自定義的 AtomicData 實現(xiàn)之前線程安全的原子整數(shù) Account 實現(xiàn):
@Slf4j
public class Test1 {
public static void main(String[] args) {
Account.demo(new MyAtomicInteger(10000));
}
}
class MyAtomicInteger implements Account {
private volatile int value;
private static final long valueOffset;
private static final Unsafe UNSAFE;
static {
UNSAFE = UnsafeAccessor.getUnsafe();
try {
valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.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 MyAtomicInteger(int value) {
this.value = value;
}
@Override
public Integer getBalance() {
return getValue();
}
@Override
public void withdraw(Integer amount) {
decrement(amount);
}
}