- AtomicReference
- AtomicMarkableReference
- AtomicStampedReference
AtomicReference 例子
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference;
@Slf4j(topic = "c.Test") public class Run {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException { DecimalAccountCas decimalAccountCas = new DecimalAccountCas(new BigDecimal("10000")); Account.demo(decimalAccountCas); }
}
class DecimalAccountCas implements Account {
private AtomicReference<BigDecimal> balance;
public DecimalAccountCas(BigDecimal balance) { this.balance = new AtomicReference<>(balance); }
@Override public BigDecimal getBlance() { 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 Account { BigDecimal getBlance();
void withdraw(BigDecimal amount);
static void demo(Account account) { List<Thread> ts = new ArrayList<>(); for (int i = 0; i < 1000; i++) { ts.add(new Thread(() -> { account.withdraw(new BigDecimal("10")); })); } ts.forEach(Thread::start); ts.forEach(t -> { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(account.getBlance()); } }
|
ABA 问题
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 27 28 29 30 31 32 33 34
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference;
@Slf4j(topic = "c.Test") public class Run {
static AtomicReference<String> ref = new AtomicReference<>("A");
public static void main(String[] args) throws InterruptedException { log.debug("main start..."); String prev = ref.get(); other(); Thread.sleep(1000); 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")); }).start();
Thread.sleep(500);
new Thread(() -> { log.debug("change B->A {}", ref.compareAndSet(ref.get(), "A")); }).start(); } }
|
输出
1 2 3 4
| 18:41:45.730 [main] DEBUG c.Test - main start... 18:41:45.777 [Thread-0] DEBUG c.Test - change A->B true 18:41:46.279 [Thread-1] DEBUG c.Test - change B->A true 18:41:47.282 [main] DEBUG c.Test - change A->C true
|
log.debug("change A->C {}", ref.compareAndSet(prev, "C"));
是无法感知到之前的 A
是不是变化了。
AtomicStampedReference
AtomicStampedReference
可以解决 ABA
问题。其原理是加了一个版本号,记录该值是否修改过,要是修改了,就要修改版本号。
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 27 28 29 30 31 32 33 34
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicStampedReference;
@Slf4j(topic = "c.Test") public class Run {
static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
public static void main(String[] args) throws InterruptedException { log.debug("main start..."); String prev = ref.getReference(); int stamp = ref.getStamp(); other(); Thread.sleep(1000); log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1)); }
private static void other() throws InterruptedException { new Thread(() -> { int stamp = ref.getStamp(); log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B", stamp, stamp + 1)); }).start();
Thread.sleep(500);
new Thread(() -> { int stamp = ref.getStamp(); log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A", stamp, stamp + 1)); }).start(); } }
|
输出
1 2 3 4
| 18:47:11.430 [main] DEBUG c.Test - main start... 18:47:11.476 [Thread-0] DEBUG c.Test - change A->B true 18:47:11.977 [Thread-1] DEBUG c.Test - change B->A true 18:47:12.982 [main] DEBUG c.Test - change A->C false
|
我们可以根据 stamp
获知引用变量中途被更改多少次。
AtomicMarkableReference
有时候,我们并不关心引用变量被更改多少次,只单纯的关心是否被更改过,使用使用 AtomicMarkableReference
。
1
| AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", true);
|