當多個線程訪問同一個類時,無論運行時環境採用何種調度方式,不論線程如何交替執行,在主調代碼中不須要額外的協同或者同步代碼時,這個類均可以表現出正確的行爲,咱們則稱這個類爲線程安全的。
(關於什麼是主內存什麼事工做內存在上篇博客中進行介紹了,不懂的同窗能夠翻一下)示例代碼:java
@Slf4j public class AtomicExample2 { // 請求總數 public static int clientTotal = 5000; // 同時併發執行的線程數 public static int threadTotal = 200; public static AtomicLong count = new AtomicLong(0); public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count.get()); } private static void add() { count.incrementAndGet(); // count.getAndIncrement(); } }
jdk8中新增的保證同步操做的類,咱們以前介紹了AtomicXXX來保證原子性,那麼爲何還有有LongAdder呢?
說AtomicXXX的實現是經過死循環來判斷值的,在低併發的狀況下AtomicXXX進行更改值的命中率仍是很高的。可是在高併發下進行命中率可能沒有那麼高,從而一直執行循環操做,此時存在必定的性能消耗,在jvm中咱們容許將64位的數值拆分紅2個32位的數進行儲存的,LongAdder的思想就是將熱點數據分離,將AtomicXXX中的核心數據分離,熱點數據會被分離成多個數組,每一個數據都單獨維護各自的值,將單點的並行壓力發散到了各個節點,這樣就提升了並行,在低併發的時候性能基本和AtomicXXX相同,在高併發時具備較好的性能,缺點是在併發更新時統計時可能會出現偏差。在低併發,須要全局惟一,準確的好比id等使用AtomicXXX,要求性能使用LongAdder數組
@Slf4j public class AtomicExample3 { // 請求總數 public static int clientTotal = 5000; // 同時併發執行的線程數 public static int threadTotal = 200; public static LongAdder count = new LongAdder(); public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);、】【poiuytrewq;' for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count); } private static void add() { count.increment(); } }
@Slf4j public class AtomicExample4 { private static AtomicReference<Integer> count = new AtomicReference<>(0); public static void main(String[] args) { count.compareAndSet(0, 2); // 2 count.compareAndSet(0, 1); // no count.compareAndSet(1, 3); // no count.compareAndSet(2, 4); // 4 count.compareAndSet(3, 5); // no log.info("count:{}", count.get()); } }
AtomMNBVCXZenceFieldUpdater主要是更新某一個實例對象的一個字段這個字段必須是用volatile修飾同時不能是private修飾的,·157-=· 123444457890-
@Slf4j public class AtomicExample5 { private static AtomicIntegerFieldUpdater<AtomicExample5> updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample5.class, "count"); @Getter public volatile int count = 100; public static void main(String[] args) { AtomicExample5 example5 = new AtomicExample5(); if (updater.compareAndSet(example5, 100, 120)) { log.info("update success 1, {}", example5.getCount()); } if (updater.compareAndSet(example5, 100, 120)) { log.info("update success 2, {}", example5.getCount()); } else { log.info("update failed, {}", example5.getCount()); } } }
最後咱們介紹一下使用AtomicBoolean來實現只執行一次的操做,咱們使用private static AtomicBoolean isHappened = new AtomicBoolean(false)來初始化一個具備原子性的一個Boolean的記錄是否已經被執行安全
@Slf4j public class AtomicExample6 { private static AtomicBoolean isHappened = new AtomicBoolean(false); // 請求總數 public static int clientTotal = 5000; // 同時併發執行的線程數 public static int threadTotal = 200; public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); test(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("isHappened:{}", isHappened.get()); } private static void test() { if (isHappened.compareAndSet(false, true)) { log.info("execute"); } } }
咱們除了可使用Atomic包還可使用鎖來實現。