JUC中的原子类
原子整数
 J.U.C 并发包提供了: 
- AtomicBoolean 
 
- AtomicInteger 
 
- AtomicLong
 
以 AtomicInteger 为例  
AtomicInteger i = new AtomicInteger(0);
  System.out.println(i.getAndIncrement());
  System.out.println(i.incrementAndGet());
  System.out.println(i.decrementAndGet());
  System.out.println(i.getAndDecrement());
  System.out.println(i.getAndAdd(5));
  System.out.println(i.addAndGet(-5));
 
  System.out.println(i.getAndUpdate(p -> p - 2));
 
  System.out.println(i.updateAndGet(p -> p + 2));
   | 
 
其中updateAndGet方法可以重写为
 public static int updateAndGet(AtomicInteger i,IntUnary0perator operator) {     while (true) {         int prev = i.get();         int next = operator.applyAsInt(prev);         if (i.compareAndSet(prev,next)) {             return next;         }     } }
 
  | 
 
原子引用
为什么需要原子引用类型?
因为程序中要保护的共享数据并不一定都是基本数据类型,也有对象类型,此时就需要通过原子引用类型进行保护;
- AtomicReference
 
- AtomicMarkableReference
 
- AtomicStampedReference
 
有如下方法
public interface DecimalAccount {          BigDecimal getBalance();          void withdraw(BigDecimal amount);     
 
 
      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());     } }
  | 
 
 试着提供不同的 DecimalAccount 实现,实现安全的取款操作  
不安全实现
class DecimalAccountUnsafe implements DecimalAccount {     BigDecimal balance;     public DecimalAccountUnsafe(BigDecimal balance) {         this.balance = balance;     }     @Override     public BigDecimal getBalance() {         return balance;     }     @Override     public void withdraw(BigDecimal amount) {         BigDecimal balance = this.getBalance();         this.balance = balance.subtract(amount);     } }
  | 
 
安全实现-使用锁
class DecimalAccountSafeLock implements DecimalAccount {     private final Object lock = new Object();     BigDecimal balance;     public DecimalAccountSafeLock(BigDecimal balance) {         this.balance = balance;     }     @Override     public BigDecimal getBalance() {         return balance;     }     @Override     public void withdraw(BigDecimal amount) {         synchronized (lock) {             BigDecimal balance = this.getBalance();             this.balance = balance.subtract(amount);         }     } }
  | 
 
安全实现-使用 CAS
class DecimalAccountSafeCas implements DecimalAccount {     AtomicReference<BigDecimal> ref;     public DecimalAccountSafeCas(BigDecimal balance) {         ref = new AtomicReference<>(balance);     }     @Override     public BigDecimal getBalance() {         return ref.get();     }     @Override     public void withdraw(BigDecimal amount) {         while (true) {             BigDecimal prev = ref.get();             BigDecimal next = prev.subtract(amount);             if (ref.compareAndSet(prev, next)) {                 break;             }         }     } }
  | 
 
 测试代码  
DecimalAccount.demo(new DecimalAccountUnsafe(new BigDecimal("10000"))); DecimalAccount.demo(new DecimalAccountSafeLock(new BigDecimal("10000"))); DecimalAccount.demo(new DecimalAccountSafeCas(new BigDecimal("10000")));
  | 
 
 运行结果  
4310 cost: 425 ms  0 cost: 285 ms  0 cost: 274 ms 
   | 
 
ABA 问题及解决
ABA问题就是:如果你在主线程想把A改成B,假如其他线程把A改成B再改回成A,主线程不知道,仍然会成功修改,没办法感知已经改动过了。虽然这对大部分场景都没有影响,但是小部分场景仍然会在意是否存在这个问题,所以我们需要解决这个问题
static AtomicReference<String> ref = new AtomicReference<>("A"); public static void main(String[] args) throws InterruptedException {     log.debug("main start...");               String prev = ref.get();     other();     sleep(1);          log.debug("change A->C {}", ref.compareAndSet(prev, "C")); } private static void other() {     new Thread(() -> {         log.debug("change A->B {}", ref.compareAndSet(ref.get(), "B"));     }, "t1").start();     sleep(0.5);     new Thread(() -> {         log.debug("change B->A {}", ref.compareAndSet(ref.get(), "A"));     }, "t2").start(); }
  | 
 
 输出  
11:29:52.325 c.Test36 [main] - main start...  11:29:52.379 c.Test36 [t1] - change A->B true  11:29:52.879 c.Test36 [t2] - change B->A true  11:29:53.880 c.Test36 [main] - change A->C true 
   | 
 
主线程仅能判断出共享变量的值与最初值 A 是否相同,不能感知到这种从 A 改为 B 又 改回 A 的情况,如果主线程希望: 
只要有其它线程【动过了】共享变量,那么自己的 cas 就算失败,这时,仅比较值是不够的,需要再加一个版本号  ,也就是用下面的类可以实现
AtomicStampedReference
其实就是不但要比较值,还要比较版本号,思想就是这样。
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();     log.debug("版本 {}", stamp);          other();     sleep(1);          log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1)); } private static void other() {     new Thread(() -> {         log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B",                                                        ref.getStamp(), ref.getStamp() + 1));         log.debug("更新版本为 {}", ref.getStamp());     }, "t1").start();     sleep(0.5);     new Thread(() -> {         log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A",                                                        ref.getStamp(), ref.getStamp() + 1));         log.debug("更新版本为 {}", ref.getStamp());     }, "t2").start(); }
  | 
 
 输出为  
15:41:34.891 c.Test36 [main] - main start...  15:41:34.894 c.Test36 [main] - 版本 0  15:41:34.956 c.Test36 [t1] - change A->B true  15:41:34.956 c.Test36 [t1] - 更新版本为 1  15:41:35.457 c.Test36 [t2] - change B->A true  15:41:35.457 c.Test36 [t2] - 更新版本为 2  15:41:36.457 c.Test36 [main] - change A->C false
   | 
 
 AtomicStampedReference 可以给原子引用加上版本号,追踪原子引用整个的变化过程,如: A -> B -> A ->C ,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了几次。 
但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有AtomicMarkableReference  
AtomicMarkableReference
class GarbageBag {     String desc;     public GarbageBag(String desc) {         this.desc = desc;     }     public void setDesc(String desc) {         this.desc = desc;     }     @Override     public String toString() {         return super.toString() + " " + desc;     } } @Slf4j     public class TestABAAtomicMarkableReference {         public static void main(String[] args) throws InterruptedException {             GarbageBag bag = new GarbageBag("装满了垃圾");                          AtomicMarkableReference<GarbageBag> ref = new AtomicMarkableReference<>(bag, true);             log.debug("主线程 start...");             GarbageBag prev = ref.getReference();             log.debug(prev.toString());                          new Thread(() -> {                 log.debug("打扫卫生的线程 start...");                 bag.setDesc("空垃圾袋");                 while (!ref.compareAndSet(bag, bag, true, false)) {}                 log.debug(bag.toString());             }).start();             Thread.sleep(1000);             log.debug("主线程想换一只新垃圾袋?");             boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);             log.debug("换了么?" + success);             log.debug(ref.getReference().toString());         }     }
  | 
 
 输出  
2019-10-13 15:30:09.264 [main] 主线程 start...  2019-10-13 15:30:09.270 [main] cn.itcast.GarbageBag@5f0fd5a0 装满了垃圾 2019-10-13 15:30:09.293 [Thread-1] 打扫卫生的线程 start...  2019-10-13 15:30:09.294 [Thread-1] cn.itcast.GarbageBag@5f0fd5a0 空垃圾袋 2019-10-13 15:30:10.294 [main] 主线程想换一只新垃圾袋? 2019-10-13 15:30:10.294 [main] 换了么?false  2019-10-13 15:30:10.294 [main] cn.itcast.GarbageBag@5f0fd5a0 空垃圾袋
   | 
 
 可以注释掉打扫卫生线程代码,再观察输出  
原子数组
- AtomicIntegerArray 
 
- AtomicLongArray 
 
- AtomicReferenceArray
 
有如下方法
 
 
 
 
 
 
 
  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++) {                  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); }
 
  | 
 
不安全的数组
demo(     ()->new int[10],     (array)->array.length,     (array, index) -> array[index]++,     array-> System.out.println(Arrays.toString(array)) );
   | 
 
 结果  
[9870, 9862, 9774, 9697, 9683, 9678, 9679, 9668, 9680, 9698] 
   | 
 
安全的数组
demo(     ()-> new AtomicIntegerArray(10),     (array) -> array.length(),     (array, index) -> array.getAndIncrement(index),     array -> System.out.println(array) );
   | 
 
 结果  
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000] 
   | 
 
字段更新器
- AtomicReferenceFieldUpdater // 域 字段
 
- AtomicIntegerFieldUpdater
 
- AtomicLongFieldUpdater
 
利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常 
public class Test5 {     private volatile int field;     public static void main(String[] args) {         AtomicIntegerFieldUpdater fieldUpdater =AtomicIntegerFieldUpdater.newUpdater(Test5.class, "field");         Test5 test5 = new Test5();         fieldUpdater.compareAndSet(test5, 0, 10);                  System.out.println(test5.field);                  fieldUpdater.compareAndSet(test5, 10, 20);         System.out.println(test5.field);                  fieldUpdater.compareAndSet(test5, 10, 30);         System.out.println(test5.field);     } }
  | 
 
 输出  
原子累加器
累加器性能比较(这个比AtomicInteger等性能要高很多,毕竟是大师之作)
private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {     T adder = adderSupplier.get();     long start = System.nanoTime();     List<Thread> ts = new ArrayList<>();          for (int i = 0; i < 40; i++) {         ts.add(new Thread(() -> {             for (int j = 0; j < 500000; j++) {                 action.accept(adder);             }         }));     }     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); }
  | 
 
 比较 AtomicLong 与 LongAdder  
for (int i = 0; i < 5; i++) {     demo(() -> new LongAdder(), adder -> adder.increment()); } for (int i = 0; i < 5; i++) {     demo(() -> new AtomicLong(), adder -> adder.getAndIncrement()); }
  | 
 
 输出  
1000000 cost:43  1000000 cost:9  1000000 cost:7  1000000 cost:7  1000000 cost:7       1000000 cost:31  1000000 cost:27  1000000 cost:28  1000000 cost:24  1000000 cost:22 
   | 
 
性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加 Cell[1]… 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量,因此减少了 CAS 重试失败,从而提高性能。