目录
有如下需求,保证account.withdraw取款方法的线程安全。
- public class TestAccount {
- public static void main(String[] args) {
- Account account1 = new LockAccount(10000);
- Account.demo(account1);
-
- Account account2 = new CasAccount(10000);
- Account.demo(account2);
- }
- }
-
- // 加锁实现
- class LockAccount implements Account {
- private Integer balance;
-
- public LockAccount(Integer balance) {
- this.balance = balance;
- }
-
- @Override
- public Integer getBalance() {
- return balance;
- }
-
- @Override
- public void withdraw(Integer amount) {
- synchronized (this) {
- balance -= amount;
- }
- }
- }
-
- // 无锁实现
- class CasAccount implements Account {
- private AtomicInteger balance;
-
- public CasAccount(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;
- // 真正修改
- boolean flag;
- if (flag = balance.compareAndSet(prev, next)) {
- break;
- }
- }
- }
- }
-
- interface Account {
- // 获取余额
- Integer getBalance();
-
- // 取款
- void withdraw(Integer amount);
-
- /**
- * 方法内部会启动1000个线程,每个线程做 -10元 的操作
- * 如果初始余额为10000那么正确的结果应当是0
- */
- static void demo(Account account) {
- List
threads = new ArrayList<>(); - for (int i = 0; i < 1000; i++) {
- threads.add(new Thread(()->{
- account.withdraw(10);
- }));
- }
- long start = System.nanoTime();
- threads.forEach(Thread::start);
- threads.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");
- }
- }
-
- 结果:
- 0 cost: 125ms
- 0 cost: 80ms
前面看到的AtomicInteger的解决方法,内部并没有用锁来保护共享变量的线程安全,那么它是如何实现的呢?
- public void withdraw(Integer amount) {
- while(true) {
- // 获取余额的最新值
- int prev = balance.get();
- // 要修改的余额
- int next = prev - amount;
- // 真正修改
- boolean flag;
- if (flag = balance.compareAndSet(prev, next)) {
- break;
- }
- }
- }
其中的关键是compareAndSet(),它的简称就是CAS(也有Compare And Swap的说法),它必须是原子操作。
注意:其实CAS的底层是lock cmpxchg指令(X86架构),在单核CPU和多核CPU下都能够保证【比较-交换】的原子性。
在多核状态下,某个核执行到带lock的指令时,CPU会让总线锁住,当这个核把此指令执行完毕,再开启总线,这个过程中不会被线程的调度机制所打断,保证了多个线程对内存操作的准确性,是原子的。
获取共享变量时,为了保证该变量的可见性,需要使用volatile修饰。它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存。即一个线程对volatile变量的修改,对另一个线程可见。
注意:volatile仅仅保证了共享变量的可见性,让其它线程能够看到最新值,但不能解决指令交错问题(不能保证原子性)。
CAS必须借助volatile才能读取到共享变量的最新值来实现【比较并交换】的效果。
无锁情况下,即使重试失败,线程始终在高速运行,没有停歇,而synchronized会让线程在没有获得锁的时候,发生上下文切换,进入阻塞。
打个比喻。线程就好像高速跑道上的赛车,高速运行时,速度超快,一旦发生上下文切换,就好比赛车要减速、熄火,等被唤醒又得重新打火、启动、加速...恢复到高速运行,代价比较大。但无锁情况下,因为线程要保持运行,需要额外CPU的支持,CPU在这里就好比高速跑道,没有额外的跑道,线程想高速运行也无从谈起,虽然不会进入阻塞,但是由于没有分到时间片,仍然会进入可运行状态,还是会导致上下文切换。
结合CAS和volatile可以实现无锁并发,适用于线程数少,多核CPU的场景下。
J.U.C并发包提供了:① AtomicBoolean;② AtomicInteger;③ AtomicLong。
以AtomicInteger为例:
- public class Test2 {
- public static void main(String[] args) {
- 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, 返回10)
- System.out.println(i.addAndGet(5));
- }
- }
-
- 结果:
- 0
- 2
- 1
- 1
- 0
- 10
原来代码的改进:
- public void withdraw(Integer amount) {
- // while(true) {
- // // 获取余额的最新值
- // int prev = balance.get();
- // // 要修改的余额
- // int next = prev - amount;
- // // 真正修改
- // boolean flag;
- // if (flag = balance.compareAndSet(prev, next)) {
- // break;
- // }
- // }
- // 上述注释的代码等价于以下:
- balance.getAndAdd(-amount);
- }
- /**
- * 模拟AtomicInteger中的updateAndGet()方法(使用回调函数)
- */
- public class Test3 {
- public static void main(String[] args) {
- AtomicInteger i = new AtomicInteger(5);
-
- // lambda表达式:读取到的值 -> 设置的值
- i.updateAndGet(value -> value * 10); // 50
- i.getAndUpdate(value -> value * 10); // 50
-
- updateAndGet(i, p -> p / 2); // 250
-
- System.out.println(i.get());
- }
-
- public static void updateAndGet(AtomicInteger i, IntUnaryOperator operator) {
- while (true) {
- // 获取当前值
- int prev = i.get();
- // 进行计算
- int next = operator.applyAsInt(prev);
- // 重新赋值结果给共享变量
- if (i.compareAndSet(prev, next)) {
- break;
- }
- }
- }
- }
-
- 结果:
- 250
为什么需要原子引用类型?
有如下方法:
- public class Test4 {
- public static void main(String[] args) {
- DecimalAccount.demo(new CasDecimalAccount(new BigDecimal(10000)));
- }
- }
-
- class CasDecimalAccount implements DecimalAccount {
- // <>内为要保护的对象
- private AtomicReference
balance; -
- public CasDecimalAccount(BigDecimal 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);
-
- /**
- * 方法内部会启动1000个线程,每个线程做 -10元 的操作
- * 如果初始余额为10000那么正确的结果应当是0
- */
- static void demo(DecimalAccount account) {
- List
threads = new ArrayList<>(); - for (int i = 0; i < 1000; i++) {
- threads.add(new Thread(()->{
- account.withdraw(BigDecimal.TEN);
- }));
- }
- long start = System.nanoTime();
- threads.forEach(Thread::start);
- threads.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");
- }
- }
-
- 结果:
- 0 cost: 143ms
- /**
- * A -> B -> A 问题
- */
- public class Test5 {
- private static final Logger LOGGER = LoggerFactory.getLogger(Test5.class);
- static AtomicReference
ref = new AtomicReference<>("A"); -
- public static void main(String[] args) throws InterruptedException {
- LOGGER.debug("main start ...");
- // 获取值A
- // 这个共享变量被其他线程修改过
- String prev = ref.get();
- other();
- Thread.sleep(1000);
- // 尝试改为C(因为A改为B又改回C,主线程无法感知到共享变量被修改)
- LOGGER.debug("change A to C {}", ref.compareAndSet(prev, "C"));
- }
-
- private static void other() {
- new Thread(()->{
- LOGGER.debug("change A to B {}", ref.compareAndSet(ref.get(), "B"));
- }, "t1").start();
- new Thread(()-> {
- LOGGER.debug("change B to A {}", ref.compareAndSet(ref.get(), "A"));
- },"t2").start();
- }
- }
-
- 结果:
- 10:14:49.832 [main] DEBUG com.multiThreads.test16.Test5 - main start ...
- 10:14:49.876 [t2] DEBUG com.multiThreads.test16.Test5 - change B to A true
- 10:14:49.875 [t1] DEBUG com.multiThreads.test16.Test5 - change A to B true
- 10:14:50.875 [main] DEBUG com.multiThreads.test16.Test5 - change A to C true
主线程仅能判断出共享变量的值与最初值A是否相同,不能感知到这种从A改为B又改回A的情况,如果主线程希望:只要有其他线程【动过了】共享变量,那么自己的CAS就算失败,这时,仅比较值是不够的,需要再加一个版本号。
- public class Test6 {
- private static final Logger LOGGER = LoggerFactory.getLogger(Test5.class);
- static AtomicStampedReference
ref = new AtomicStampedReference<>("A", 0); -
- public static void main(String[] args) throws InterruptedException {
- LOGGER.debug("main start ...");
- // 获取值A
- // 这个共享变量被其他线程修改过
- String prev = ref.getReference();
- // 获取版本号
- int stamp = ref.getStamp();
- LOGGER.debug("main before {}", stamp);
- other();
- Thread.sleep(1000);
- // 尝试改为C
- LOGGER.debug("main before {}", stamp);
- // 额外参数为原版本号和新版本号,会比较版本号的值来解决ABA问题
- LOGGER.debug("change A to C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1));
- }
-
- private static void other() {
- new Thread(() -> {
- int stamp1 = ref.getStamp();
- LOGGER.debug("t1 before {}", stamp1);
- LOGGER.debug("change A to B {}", ref.compareAndSet(ref.getReference(), "B", stamp1, stamp1 + 1));
- }, "t1").start();
- new Thread(() -> {
- int stamp2 = ref.getStamp();
- LOGGER.debug("t2 before {}", stamp2);
- LOGGER.debug("change B to A {}", ref.compareAndSet(ref.getReference(), "A", stamp2, stamp2 + 1));
- }, "t2").start();
- }
- }
-
- 结果:
- 10:27:15.667 [main] DEBUG com.multiThreads.test16.Test5 - main start ...
- 10:27:15.670 [main] DEBUG com.multiThreads.test16.Test5 - main before 0
- 10:27:15.729 [t1] DEBUG com.multiThreads.test16.Test5 - t1 before 0
- 10:27:15.729 [t1] DEBUG com.multiThreads.test16.Test5 - change A to B true
- 10:27:15.730 [t2] DEBUG com.multiThreads.test16.Test5 - t2 before 1
- 10:27:15.730 [t2] DEBUG com.multiThreads.test16.Test5 - change B to A true
- 10:27:16.730 [main] DEBUG com.multiThreads.test16.Test5 - main before 0
- 10:27:16.730 [main] DEBUG com.multiThreads.test16.Test5 - change A to C false
AtomicStampedReference可以给原子引用加上版本号,追踪原子引用整个的变化过程,如:A -> B -> A -> C,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了几次。
但是有时候,并不关心引用变量更改了几次,只是单纯地关心是否更改过,所以就有了AtomicMarkableReference。
- public class Test7 {
- private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
-
- public static void main(String[] args) throws InterruptedException {
- GarbageBag bag = new GarbageBag("装满了垃圾");
- // 参数2 mark 可以看作一个标记,表示垃圾袋满了
- AtomicMarkableReference
ref = new AtomicMarkableReference<>(bag, true); -
- LOGGER.debug("start");
- GarbageBag prev = ref.getReference();
- LOGGER.debug(prev.toString());
-
- new Thread(()->{
- LOGGER.debug("start...");
- bag.setBag("空垃圾袋");
- // 不换垃圾袋,仅拿出垃圾
- ref.compareAndSet(bag, bag, true, false);
- LOGGER.debug(bag.toString());
- }, "保洁阿姨").start();
-
- Thread.sleep(1000);
- LOGGER.debug("想换一只新垃圾袋?");
- boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);
- LOGGER.debug("换了么?" + success);
- LOGGER.debug(ref.getReference().toString());
- }
- }
-
- @Data
- class GarbageBag {
- private String bag;
- public GarbageBag(String bag) {
- this.bag = bag;
- }
-
- @Override
- public String toString() {
- return bag;
- }
- }
-
- 结果:
- 11:05:12.166 [main] DEBUG com.multiThreads.test16.Test7 - start
- 11:05:12.169 [main] DEBUG com.multiThreads.test16.Test7 - 装满了垃圾
- 11:05:12.229 [保洁阿姨] DEBUG com.multiThreads.test16.Test7 - start...
- 11:05:12.229 [保洁阿姨] DEBUG com.multiThreads.test16.Test7 - 空垃圾袋
- 11:05:13.228 [main] DEBUG com.multiThreads.test16.Test7 - 想换一只新垃圾袋?
- 11:05:13.228 [main] DEBUG com.multiThreads.test16.Test7 - 换了么?false
- 11:05:13.228 [main] DEBUG com.multiThreads.test16.Test7 - 空垃圾袋
有如下方法:
- public class Test8 {
- 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),
- (array) -> array.length(),
- (array, index) -> array.getAndIncrement(index),
- array -> System.out.println(array)
- );
- }
-
- /**
- * 方法内会启动10个线程,并发让数组所有元素总共自增10000次
- *
- * @param arraySupplier:Supplier:提供者,()->结果。提供数组、可以是线程不安全数组或线程安全数组
- * @param lengthFun:Function:函数,(参数)->结果,还有一种为BiFunction 为 (参数1,参数2) -> 结果。获取数组长度的方法
- * @param putConsumer:Consumer:消费者,一个参数,没结果,(参数)->void。BiConsumer为(参数1,参数2)->{} 自增方法,回传array,length,index。
- * @param printConsumer:打印数组的方法
- * @param
- */
- private static
void demo( - Supplier
arraySupplier, Function lengthFun, - BiConsumer
putConsumer, Consumer printConsumer) { - List
ts = new ArrayList<>(); - T array = arraySupplier.get();
- int length = lengthFun.apply(array);
- for (int i = 0; i < length; i++) {
- // 每个线程对数组作 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);
- }
- }
-
- 结果:
- [9780, 9779, 9782, 9788, 9778, 9772, 9777, 9776, 9776, 9780]
- [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]
利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合volatile修饰的字段使用,否则会出现异常。
- public class Test9 {
- 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, "张三"));
- System.out.println(stu);
- }
- }
-
- class Student {
- volatile String name;
-
- @Override
- public String toString() {
- return "Student{" +
- "name='" + name + '\'' +
- '}';
- }
- }
-
- 结果:
- true
- Student{name='张三'}
累加器性能比较。
- public class Test10 {
- public static void main(String[] args) {
- // 使用原子整数
- for (int i = 0; i < 5; i++) {
- demo(
- () -> new AtomicLong(0),
- (adder) -> adder.getAndIncrement()
- );
- }
-
- System.out.println();
-
- // 使用原子累加器
- for (int i = 0; i < 5; i++) {
- demo(
- () -> new LongAdder(),
- adder -> adder.increment()
- );
- }
- }
-
- /**
- *
- * @param adderSupplier:提供累加器对象
- * @param action:执行累加操作
- * @param
- */
- private static
void demo(Supplier adderSupplier, Consumer action) { - T adder = adderSupplier.get();
- ArrayList
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:26
- 2000000 cost:26
- 2000000 cost:30
- 2000000 cost:31
- 2000000 cost:30
-
- 2000000 cost:14
- 2000000 cost:9
- 2000000 cost:9
- 2000000 cost:9
- 2000000 cost:8
性能提升的原因很简单,就是在竞争时,设置多个累加单元,Thread-0累加Cell[0],而Thread-1累加Cell[1]...最后将结果汇总。这样它们在累加时操作不同的Cell变量,因此减少了CAS重试失败次数,从而提高性能。
LongAdder是并发大师@author Doug Lea (大哥李)的作品,设计得非常精巧。
LongAdder类有几个关键域:
- // 累加单元数组,懒惰初始化
- transient volatile Cell[] cells;
-
- // 基础值,如果没有竞争,则用cas累加这个域
- transient volatile long base;
-
- // 在cells创建或扩容时,置为1,表示加锁
- transient volatile int cellsBusy;
- // 实际尽量不要使用CasLock
- class CasLock {
- private static final Logger LOGGER = LoggerFactory.getLogger(CasLock.class);
- // 0:没加锁;1:加了锁
- private AtomicInteger state = new AtomicInteger(0);
-
- public void lock() {
- while(true) {
- if (state.compareAndSet(0, 1)) {
- break;
- }
- }
- }
-
- public void unlock() {
- LOGGER.debug("unlock...");
- state.set(0);
- }
-
- public static void main(String[] args) {
- CasLock lock = new CasLock();
- new Thread(()->{
- LOGGER.debug("begin...");
- lock.lock();
- LOGGER.debug("lock...");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }).start();
-
- new Thread(()->{
- LOGGER.debug("begin...");
- lock.lock();
- try {
- LOGGER.debug("lock...");
- } finally {
- lock.unlock();
- }
- }).start();
- }
- }
-
- 结果:
- 15:35:45.783 [Thread-0] DEBUG com.multiThreads.test16.CasLock - begin...
- 15:35:45.783 [Thread-1] DEBUG com.multiThreads.test16.CasLock - begin...
- 15:35:45.786 [Thread-0] DEBUG com.multiThreads.test16.CasLock - lock...
- 15:35:46.786 [Thread-0] DEBUG com.multiThreads.test16.CasLock - unlock...
- 15:35:46.786 [Thread-1] DEBUG com.multiThreads.test16.CasLock - lock...
- 15:35:46.786 [Thread-1] DEBUG com.multiThreads.test16.CasLock - unlock...
其中Cell即为累加单元。
- @sum.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与内存的速度差异很大,需要靠预读数据至缓存来提升效率。而缓存以缓存行为单位,每个缓存行对应着一块内存,一般是64 byte(8个long),缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中,CPU要保证数据的一致性,如果某个CPU核心更改了数据,其他CPU核心对应的整个缓存行必须失效。
因为Cell是数组形式,在内存中是连续存储的,一个Cell为24字节(16字节的对象头和8字节的value),因此缓存行可以存下2个Cell对象。这样问题来了:
无论谁修改成功,都会导致对方Core的缓存行失效,比如Core-0中Cell[0]=6000,Cell[1]=8000要累加Cell[0]=6001,Cell[1]=8000,这时会让Core-1的缓存行失效。
@sun.misc.Contended用来解决这个问题,它的原理是使用此注解的对象或字段的前后各增加128字节的大小的padding,从而让CPU将对象预读至缓存时占用不同的缓存行,这样,不会造成对方缓存行的失效。
Unsafe对象提供了非常底层的,操作内存、线程的方法,Unsafe对象不能直接调用,只能通过反射获得。
- class UnsafeAccessor {
- static Unsafe unsafe;
-
- static {
- try {
- Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
- theUnsafe.setAccessible(true); // 允许访问私有成员
- unsafe = (Unsafe) theUnsafe.get(null); // 获得Unsafe对象
- } catch (NoSuchFieldException | IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- static Unsafe getUnsafe() {
- return unsafe;
- }
- }
- public class Test13 {
- public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
- Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
- theUnsafe.setAccessible(true); // 允许访问私有成员
- Unsafe unsafe = (Unsafe) theUnsafe.get(null); // 获得Unsafe对象
-
- // 1、获取域的偏移地址
- long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));
- long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));
- // 2、执行CAS操作
- Teacher t = new Teacher();
- unsafe.compareAndSwapInt(t, idOffset, 0, 1); // 修改id:0->1
- unsafe.compareAndSwapObject(t, nameOffset, null, "张三"); // 修改name:null->张三
- // 3、验证
- System.out.println(t);
- }
- }
-
- @Data
- class Teacher {
- volatile int id;
- volatile String name;
- }
-
- 结果:
- Teacher(id=1, name=张三)
- public class Test14 {
- public static void main(String[] args) {
- Account.demo(new MyAtomicInteger(10000));
- }
- }
-
- interface Account {
- // 获取余额
- Integer getBalance();
-
- // 取款
- void withdraw(Integer amount);
-
- /**
- * 方法内部会启动1000个线程,每个线程做 -10元 的操作
- * 如果初始余额为10000那么正确的结果应当是0
- */
- static void demo(Account account) {
- List
threads = new ArrayList<>(); - for (int i = 0; i < 1000; i++) {
- threads.add(new Thread(()->{
- account.withdraw(10);
- }));
- }
- long start = System.nanoTime();
- threads.forEach(Thread::start);
- threads.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 UnsafeAccessor {
- static Unsafe unsafe;
-
- static {
- try {
- Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
- theUnsafe.setAccessible(true); // 允许访问私有成员
- unsafe = (Unsafe) theUnsafe.get(null); // 获得Unsafe对象
- } catch (NoSuchFieldException | IllegalAccessException e) {
- e.printStackTrace();
- }
- }
-
- static Unsafe getUnsafe() {
- return unsafe;
- }
- }
-
- class MyAtomicInteger implements Account{
- private static final Unsafe UNSAFE;
- private static final long valueOffset;
- private volatile int value;
- static {
- try {
- UNSAFE = UnsafeAccessor.getUnsafe();
- valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
- }
-
- public MyAtomicInteger(int value) {
- this.value = value;
- }
-
- 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;
- }
- }
- }
-
- @Override
- public Integer getBalance() {
- return getValue();
- }
-
- @Override
- public void withdraw(Integer amount) {
- decrement(amount);
- }
- }
-
- 结果:
- 0 cost: 111ms