摘要:線程安全性原子性提供了互斥訪問,同一時刻只能有一個線程來對他進(jìn)行操作。原子性包是通過來保證線程原子性通過比較操作的對象的值工作內(nèi)存的值與底層的值共享內(nèi)存中的值對比是否相同來判斷是否進(jìn)行處理,如果不相同則重新獲取。
線程安全性 定義
當(dāng)多個線程訪問同一個類時,不管運行時環(huán)境采用何種調(diào)度方式,不論線程如何交替執(zhí)行,在主調(diào)代碼中不需要額外的協(xié)同或者同步代碼時,這個類都可以表現(xiàn)出正確的行為,我們則稱這個類為線程安全的。線程安全性
原子性:提供了互斥訪問,同一時刻只能有一個線程來對他進(jìn)行操作。
可見性:一個線程對主內(nèi)存的修改可以及時被其他線程觀察到。
有序性:一個線程觀察其他線程中的指令順序,由于指令重排序的存在,該結(jié)果一般雜亂無序。
原子性 - Atomic包AtomicXXX 是通過 CAS(CompareAndSwap)來保證線程原子性 通過比較操作的對象的值(工作內(nèi)存的值)與底層的值(共享內(nèi)存中的值)對比是否相同來判斷是否進(jìn)行處理,如果不相同則重新獲取。如此循環(huán)操作,直至獲取到期望的值。
(關(guān)于什么是主內(nèi)存什么事工作內(nèi)存在上篇博客中進(jìn)行介紹了,不懂的同學(xué)可以翻一下)示例代碼:
@Slf4j public class AtomicExample2 { // 請求總數(shù) public static int clientTotal = 5000; // 同時并發(fā)執(zhí)行的線程數(shù) 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(); } }
LongAdder和DoubleAdder
jdk8中新增的保證同步操作的類,我們之前介紹了AtomicXXX來保證原子性,那么為什么還有有LongAdder呢?
說AtomicXXX的實現(xiàn)是通過死循環(huán)來判斷值的,在低并發(fā)的情況下AtomicXXX進(jìn)行更改值的命中率還是很高的。但是在高并發(fā)下進(jìn)行命中率可能沒有那么高,從而一直執(zhí)行循環(huán)操作,此時存在一定的性能消耗,在jvm中我們允許將64位的數(shù)值拆分成2個32位的數(shù)進(jìn)行儲存的,LongAdder的思想就是將熱點數(shù)據(jù)分離,將AtomicXXX中的核心數(shù)據(jù)分離,熱點數(shù)據(jù)會被分離成多個數(shù)組,每個數(shù)據(jù)都多帶帶維護(hù)各自的值,將單點的并行壓力發(fā)散到了各個節(jié)點,這樣就提高了并行,在低并發(fā)的時候性能基本和AtomicXXX相同,在高并發(fā)時具有較好的性能,缺點是在并發(fā)更新時統(tǒng)計時可能會出現(xiàn)誤差。在低并發(fā),需要全局唯一,準(zhǔn)確的比如id等使用AtomicXXX,要求性能使用LongAdder
@Slf4j public class AtomicExample3 { // 請求總數(shù) public static int clientTotal = 5000; // 同時并發(fā)執(zhí)行的線程數(shù) 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(); } }
AtomicReference、AtomicReferenceFieldUpdater
AtomicReference是給定指定的期望值當(dāng)期望值與主內(nèi)存中的值相同然后更新,示例代碼
@Slf4j public class AtomicExample4 { private static AtomicReferencecount = 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 AtomicIntegerFieldUpdaterupdater = 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來實現(xiàn)只執(zhí)行一次的操作,我們使用private static AtomicBoolean isHappened = new AtomicBoolean(false)來初始化一個具有原子性的一個Boolean的記錄是否已經(jīng)被執(zhí)行
@Slf4j public class AtomicExample6 { private static AtomicBoolean isHappened = new AtomicBoolean(false); // 請求總數(shù) public static int clientTotal = 5000; // 同時并發(fā)執(zhí)行的線程數(shù) 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包還可以使用鎖來實現(xiàn)。
synchronize:依賴jvm
修飾代碼塊:適用范圍大括號括起來的代碼,作用于調(diào)用的對象
修飾方法:適用范圍整個方法,作用于調(diào)用的對象
修飾靜態(tài)方法:適用范圍整個靜態(tài)方法,作用于所有對象
修飾一個類:適用范圍是括起來的部分,作用于所有對象
Lock:依賴特殊的cpu指令、代碼實現(xiàn),ReentrantLock
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/74433.html
摘要:是需要我們?nèi)ヌ幚砗芏嗍虑椋瑸榱朔乐苟嗑€程給我們帶來的安全和性能的問題下面就來簡單總結(jié)一下我們需要哪些知識點來解決多線程遇到的問題。 前言 不小心就鴿了幾天沒有更新了,這個星期回家咯。在學(xué)校的日子要努力一點才行! 只有光頭才能變強(qiáng) 回顧前面: 多線程三分鐘就可以入個門了! Thread源碼剖析 本文章的知識主要參考《Java并發(fā)編程實戰(zhàn)》這本書的前4章,這本書的前4章都是講解并發(fā)的基...
摘要:將與當(dāng)前線程建立一對一關(guān)系的值移除。為了讓方法里的操作具有原子性,也就是在一個線程執(zhí)行這一系列操作的同時禁止其他線程執(zhí)行這些操作,提出了鎖的概念。 上頭一直在說以線程為基礎(chǔ)的并發(fā)編程的好處了,什么提高處理器利用率啦,簡化編程模型啦。但是磚家們還是認(rèn)為并發(fā)編程是程序開發(fā)中最不可捉摸、最詭異、最扯犢子、最麻煩、最惡心、最心煩、最容易出錯、最不符合社會主義核心價值觀的一個部分~ 造成這么多最...
摘要:另一個是使用鎖的機(jī)制來處理線程之間的原子性。依賴于去實現(xiàn)鎖,因此在這個關(guān)鍵字作用對象的作用范圍內(nèi),都是同一時刻只能有一個線程對其進(jìn)行操作的。 線程安全性 定義:當(dāng)多個線程訪問某個類時,不管運行時環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個類都能表現(xiàn)出正確的行為,那么就稱這個類是線程安全的。 線程安全性主要體現(xiàn)在三個方面:原子性、可見性...
摘要:線程的這種交叉操作會導(dǎo)致線程不安全。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段。如果聲明一個域為一些情況就可以確保多線程訪問到的變量是最新的。并發(fā)要求一個線程對對象進(jìn)行了操作,對象發(fā)生了變化,這種變化應(yīng)該對其他線程是可見的。 雖是讀書筆記,但是如轉(zhuǎn)載請注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 一個問題: ...
摘要:的線程機(jī)制是搶占式。會讓出當(dāng)多個線程并發(fā)的對主存中的數(shù)據(jù)進(jìn)行操作時,有且只有一個會成功,其余均失敗。和對象只有在困難的多線程問題中才是必須的。 并發(fā)簡述 并發(fā)通常是用于提高運行在單處理器上的程序的性能。在單 CPU 機(jī)器上使用多任務(wù)的程序在任意時刻只在執(zhí)行一項工作。 并發(fā)編程使得一個程序可以被劃分為多個分離的、獨立的任務(wù)。一個線程就是在進(jìn)程中的一個單一的順序控制流。java的線程機(jī)制是...
閱讀 594·2021-11-18 13:12
閱讀 1314·2021-11-15 11:39
閱讀 2473·2021-09-23 11:22
閱讀 6194·2021-09-22 15:15
閱讀 3655·2021-09-02 09:54
閱讀 2310·2019-08-30 11:10
閱讀 3245·2019-08-29 14:13
閱讀 2913·2019-08-29 12:49