synchronized 可以保證方法或者代碼塊在運(yùn)行時(shí),同一時(shí)刻只有一個(gè)方法可以進(jìn)入到臨界區(qū)辣垒,同時(shí)它還可以保證共享變量的內(nèi)存可見性。深入分析 synchronized 的內(nèi)在實(shí)現(xiàn)機(jī)制,鎖優(yōu)化、鎖升級(jí)過程原押。
volatile 可以保證線程可見性且提供了一定的有序性,但是無(wú)法保證原子性偎血。在 JVM 底層 volatile 是采用“內(nèi)存屏障”來(lái)實(shí)現(xiàn)的诸衔。這篇博文將帶你分析 volatile 的本質(zhì)
happens-before 原則是判斷數(shù)據(jù)是否存在競(jìng)爭(zhēng)、線程是否安全的主要依據(jù)颇玷,保證了多線程環(huán)境下的可見性笨农。
定義如下:
如果一個(gè)操作happens-before另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見帖渠,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前谒亦。
兩個(gè)操作之間存在happens-before關(guān)系,并不意味著一定要按照happens-before原則制定的順序來(lái)執(zhí)行空郊。如果重排序之后的執(zhí)行結(jié)果與按照happens-before關(guān)系來(lái)執(zhí)行的結(jié)果一致份招,那么這種重排序并不非法。
在執(zhí)行程序時(shí)狞甚,為了提供性能锁摔,處理器和編譯器常常會(huì)對(duì)指令進(jìn)行重排序,但是不能隨意重排序哼审,不是你想怎么排序就怎么排序谐腰,它需要滿足以下兩個(gè)條件:
在單線程環(huán)境下不能改變程序運(yùn)行的結(jié)果孕豹;
存在數(shù)據(jù)依賴關(guān)系的不允許重排序
as-if-serial 語(yǔ)義保證在單線程環(huán)境下重排序后的執(zhí)行結(jié)果不會(huì)改變。
volatile的內(nèi)存語(yǔ)義是:
當(dāng)寫一個(gè) volatile 變量時(shí)十气,JMM 會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值立即刷新到主內(nèi)存中励背。
當(dāng)讀一個(gè) volatile 變量時(shí),JMM 會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存設(shè)置為無(wú)效砸西,直接從主內(nèi)存中讀取共享變量
總是說(shuō) volatile 保證可見性椅野,happens-before 是 JMM 實(shí)現(xiàn)可見性的基礎(chǔ)理論,兩者會(huì)碰撞怎樣的火花籍胯?這篇博文給你答案。
DCL离福,即Double Check Lock杖狼,雙重檢查鎖定。是實(shí)現(xiàn)單例模式比較好的方式妖爷,這篇博客告訴你 DCL 中為何要加 volatile 這個(gè)關(guān)鍵字蝶涩。
AQS,AbstractQueuedSynchronizer絮识,即隊(duì)列同步器绿聘。它是構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架(如ReentrantLock、ReentrantReadWriteLock次舌、Semaphore等)熄攘,為 JUC 并發(fā)包中的核心基礎(chǔ)組件。
前線程已經(jīng)等待狀態(tài)等信息構(gòu)造成一個(gè)節(jié)點(diǎn)(Node)并將其加入到CLH同步隊(duì)列彼念,同時(shí)會(huì)阻塞當(dāng)前線程挪圾,當(dāng)同步狀態(tài)釋放時(shí),會(huì)把首節(jié)點(diǎn)喚醒(公平鎖)逐沙,使其再次嘗試獲取同步狀態(tài)哲思。
AQS的設(shè)計(jì)模式采用的模板方法模式,子類通過繼承的方式吩案,實(shí)現(xiàn)它的抽象方法來(lái)管理同步狀態(tài)棚赔,對(duì)于子類而言它并沒有太多的活要做,AQS提供了大量的模板方法來(lái)實(shí)現(xiàn)同步徘郭,主要是分為三類:獨(dú)占式獲取和釋放同步狀態(tài)靠益、共享式獲取和釋放同步狀態(tài)、查詢同步隊(duì)列中的等待線程情況崎岂。
當(dāng)需要阻塞或者喚醒一個(gè)線程的時(shí)候捆毫,AQS 都是使用 LockSupport 這個(gè)工具類來(lái)完成。
LockSupport是用來(lái)創(chuàng)建鎖和其他同步類的基本線程阻塞原語(yǔ)冲甘。
一個(gè)可重入的互斥鎖定 Lock绩卤,它具有與使用 synchronized 方法和語(yǔ)句所訪問的隱式監(jiān)視器鎖定相同的一些基本行為和語(yǔ)義途样,但功能更強(qiáng)大。ReentrantLock 將由最近成功獲得鎖定濒憋,并且還沒有釋放該鎖定的線程所擁有何暇。當(dāng)鎖定沒有被另一個(gè)線程所擁有時(shí),調(diào)用 lock 的線程將成功獲取該鎖定并返回凛驮。如果當(dāng)前線程已經(jīng)擁有該鎖定裆站,此方法將立即返回∏玻可以使用 isHeldByCurrentThread()
和 getHoldCount()
方法來(lái)檢查此情況是否發(fā)生宏胯。
這篇博客帶你理解 重入鎖:ReentrantLock 內(nèi)在本質(zhì)。
讀寫鎖維護(hù)著一對(duì)鎖本姥,一個(gè)讀鎖和一個(gè)寫鎖肩袍。通過分離讀鎖和寫鎖,使得并發(fā)性比一般的排他鎖有了較大的提升:在同一時(shí)間可以允許多個(gè)讀線程同時(shí)訪問婚惫,但是在寫線程訪問時(shí)氛赐,所有讀線程和寫線程都會(huì)被阻塞。
讀寫鎖的主要特性:
公平性:支持公平性和非公平性先舷。
重入性:支持重入艰管。讀寫鎖最多支持65535個(gè)遞歸寫入鎖和65535個(gè)遞歸讀取鎖。
鎖降級(jí):遵循獲取寫鎖蒋川、獲取讀鎖在釋放寫鎖的次序牲芋,寫鎖能夠降級(jí)成為讀鎖
在沒有Lock之前,我們使用synchronized來(lái)控制同步尔破,配合Object的wait()街图、notify()系列方法可以實(shí)現(xiàn)等待/通知模式。在Java SE5后懒构,Java提供了Lock接口餐济,相對(duì)于Synchronized而言,Lock提供了條件Condition胆剧,對(duì)線程的等待絮姆、喚醒操作更加詳細(xì)和靈活
CAS,Compare And Swap秩霍,即比較并交換篙悯。Doug lea大神在同步組件中大量使用 CAS 技術(shù)鬼斧神工地實(shí)現(xiàn)了Java 多線程的并發(fā)操作。整個(gè) AQS 同步組件铃绒、Atomic 原子類操作等等都是以 CAS 實(shí)現(xiàn)的鸽照。可以說(shuō)CAS是整個(gè)JUC的基石颠悬。
CyclicBarrier矮燎,一個(gè)同步輔助類定血。它允許一組線程互相等待,直到到達(dá)某個(gè)公共屏障點(diǎn) (common barrier point)诞外。在涉及一組固定大小的線程的程序中澜沟,這些線程必須不時(shí)地互相等待,此時(shí) CyclicBarrier 很有用峡谊。因?yàn)樵?barrier 在釋放等待線程后可以重用茫虽,所以稱它為循環(huán) 的 barrier。
CountDownLatch 所描述的是”在完成一組正在其他線程中執(zhí)行的操作之前既们,它允許一個(gè)或多個(gè)線程一直等待“濒析。
用給定的計(jì)數(shù) 初始化 CountDownLatch。由于調(diào)用了 countDown() 方法啥纸,所以在當(dāng)前計(jì)數(shù)到達(dá)零之前悼枢,await 方法會(huì)一直受阻塞。之后脾拆,會(huì)釋放所有等待的線程,await 的所有后續(xù)調(diào)用都將立即返回莹妒。
Semaphore名船,信號(hào)量,是一個(gè)控制訪問多個(gè)共享資源的計(jì)數(shù)器旨怠。從概念上講渠驼,信號(hào)量維護(hù)了一個(gè)許可集。如有必要鉴腻,在許可可用前會(huì)阻塞每一個(gè) acquire()迷扇,然后再獲取該許可。每個(gè) release() 添加一個(gè)許可爽哎,從而可能釋放一個(gè)正在阻塞的獲取者蜓席。但是,不使用實(shí)際的許可對(duì)象课锌,Semaphore 只對(duì)可用許可的號(hào)碼進(jìn)行計(jì)數(shù)厨内,并采取相應(yīng)的行動(dòng)。
可以在對(duì)中對(duì)元素進(jìn)行配對(duì)和交換的線程的同步點(diǎn)渺贤。每個(gè)線程將條目上的某個(gè)方法呈現(xiàn)給 exchange 方法雏胃,與伙伴線程進(jìn)行匹配,并且在返回時(shí)接收其伙伴的對(duì)象志鞍。Exchanger 可能被視為 SynchronousQueue 的雙向形式瞭亮。Exchanger 可能在應(yīng)用程序(比如遺傳算法和管道設(shè)計(jì))中很有用。
ConcurrentHashMap 作為 Concurrent 一族固棚,其有著高效地并發(fā)操作统翩。在1.8 版本以前仙蚜,ConcurrentHashMap 采用分段鎖的概念,使鎖更加細(xì)化唆缴,但是 1.8 已經(jīng)改變了這種思路鳍征,而是利用 CAS + Synchronized 來(lái)保證并發(fā)更新的安全,當(dāng)然底層采用數(shù)組+鏈表+紅黑樹的存儲(chǔ)結(jié)構(gòu)面徽。這篇博客帶你徹底理解 ConcurrentHashMap艳丛。
在 1.8 ConcurrentHashMap 的put操作中,如果發(fā)現(xiàn)鏈表結(jié)構(gòu)中的元素超過了TREEIFY_THRESHOLD(默認(rèn)為8)趟紊,則會(huì)把鏈表轉(zhuǎn)換為紅黑樹氮双,已便于提高查詢效率。那么具體的轉(zhuǎn)換過程是怎么樣的霎匈?這篇博客給你答案戴差。
ConcurrentLinkedQueue是一個(gè)基于鏈接節(jié)點(diǎn)的無(wú)邊界的線程安全隊(duì)列,它采用FIFO原則對(duì)元素進(jìn)行排序铛嘱。采用“wait-free”算法(即CAS算法)來(lái)實(shí)現(xiàn)的暖释。
CoucurrentLinkedQueue規(guī)定了如下幾個(gè)不變性:
在入隊(duì)的最后一個(gè)元素的next為null
隊(duì)列中所有未刪除的節(jié)點(diǎn)的item都不能為null且都能從head節(jié)點(diǎn)遍歷到
對(duì)于要?jiǎng)h除的節(jié)點(diǎn),不是直接將其設(shè)置為null墨吓,而是先將其item域設(shè)置為null(迭代器會(huì)跳過item為null的節(jié)點(diǎn))
允許head和tail更新滯后球匕。這是什么意思呢?意思就說(shuō)是head帖烘、tail不總是指向第一個(gè)元素和最后一個(gè)元素(后面闡述)亮曹。
我們?cè)贘ava世界里看到了兩種實(shí)現(xiàn)key-value的數(shù)據(jù)結(jié)構(gòu):Hash、TreeMap秘症,這兩種數(shù)據(jù)結(jié)構(gòu)各自都有著優(yōu)缺點(diǎn)照卦。
Hash表:插入、查找最快乡摹,為O(1)役耕;如使用鏈表實(shí)現(xiàn)則可實(shí)現(xiàn)無(wú)鎖;數(shù)據(jù)有序化需要顯式的排序操作聪廉。
紅黑樹:插入蹄葱、查找為O(logn),但常數(shù)項(xiàng)較谐小图云;無(wú)鎖實(shí)現(xiàn)的復(fù)雜性很高,一般需要加鎖邻邮;數(shù)據(jù)天然有序竣况。
這里介紹第三種實(shí)現(xiàn) key-value 的數(shù)據(jù)結(jié)構(gòu):SkipList。SkipList 有著不低于紅黑樹的效率筒严,但是其原理和實(shí)現(xiàn)的復(fù)雜度要比紅黑樹簡(jiǎn)單多了丹泉。
ConcurrentSkipListMap 其內(nèi)部采用 SkipLis 數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)情萤。
ArrayBlockingQueue,一個(gè)由數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列摹恨。該隊(duì)列采用FIFO的原則對(duì)元素進(jìn)行排序添加的筋岛。
ArrayBlockingQueue 為有界且固定,其大小在構(gòu)造時(shí)由構(gòu)造函數(shù)來(lái)決定晒哄,確認(rèn)之后就不能再改變了睁宰。ArrayBlockingQueue 支持對(duì)等待的生產(chǎn)者線程和使用者線程進(jìn)行排序的可選公平策略,但是在默認(rèn)情況下不保證線程公平的訪問寝凌,在構(gòu)造時(shí)可以選擇公平策略(fair = true)柒傻。公平性通常會(huì)降低吞吐量,但是減少了可變性和避免了“不平衡性”较木。
PriorityBlockingQueue是一個(gè)支持優(yōu)先級(jí)的無(wú)界阻塞隊(duì)列红符。默認(rèn)情況下元素采用自然順序升序排序,當(dāng)然我們也可以通過構(gòu)造函數(shù)來(lái)指定Comparator來(lái)對(duì)元素進(jìn)行排序伐债。需要注意的是PriorityBlockingQueue不能保證同優(yōu)先級(jí)元素的順序预侯。
DelayQueue是一個(gè)支持延時(shí)獲取元素的無(wú)界阻塞隊(duì)列。里面的元素全部都是“可延期”的元素峰锁,列頭的元素是最先“到期”的元素雌桑,如果隊(duì)列里面沒有元素到期,是不能從列頭獲取元素的祖今,哪怕有元素也不行。也就是說(shuō)只有在延遲期到時(shí)才能夠從隊(duì)列中取元素拣技。
DelayQueue主要用于兩個(gè)方面:
緩存:清掉緩存中超時(shí)的緩存數(shù)據(jù)
任務(wù)超時(shí)處理
SynchronousQueue與其他BlockingQueue有著不同特性:
SynchronousQueue沒有容量千诬。與其他BlockingQueue不同,SynchronousQueue是一個(gè)不存儲(chǔ)元素的BlockingQueue膏斤。每一個(gè)put操作必須要等待一個(gè)take操作徐绑,否則不能繼續(xù)添加元素,反之亦然莫辨。
因?yàn)闆]有容量傲茄,所以對(duì)應(yīng) peek, contains, clear, isEmpty ... 等方法其實(shí)是無(wú)效的。例如clear是不執(zhí)行任何操作的沮榜,contains始終返回false,peek始終返回null盘榨。
SynchronousQueue分為公平和非公平,默認(rèn)情況下采用非公平性訪問策略蟆融,當(dāng)然也可以通過構(gòu)造函數(shù)來(lái)設(shè)置為公平性訪問策略(為true即可)草巡。
若使用 TransferQueue, 則隊(duì)列中永遠(yuǎn)會(huì)存在一個(gè) dummy node(這點(diǎn)后面詳細(xì)闡述)。
SynchronousQueue非常適合做交換工作型酥,生產(chǎn)者的線程和消費(fèi)者的線程同步以傳遞某些信息山憨、事件或者任務(wù)查乒。
LinkedTransferQueue 是基于鏈表的 FIFO 無(wú)界阻塞隊(duì)列,它出現(xiàn)在 JDK7 中郁竟。Doug Lea 大神說(shuō) LinkedTransferQueue 是一個(gè)聰明的隊(duì)列玛迄。它是 ConcurrentLinkedQueue、SynchronousQueue (公平模式下)棚亩、無(wú)界的LinkedBlockingQueues 等的超集蓖议。
LinkedBlockingDeque 是一個(gè)由鏈表組成的雙向阻塞隊(duì)列,雙向隊(duì)列就意味著可以從對(duì)頭蔑舞、對(duì)尾兩端插入和移除元素拒担,同樣意味著 LinkedBlockingDeque 支持 FIFO、FILO 兩種操作方式攻询。
LinkedBlockingDeque 是可選容量的从撼,在初始化時(shí)可以設(shè)置容量防止其過度膨脹,如果不設(shè)置钧栖,默認(rèn)容量大小為 Integer.MAX_VALUE低零。
ThreadLocal 提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對(duì)應(yīng)物拯杠,因?yàn)樵L問某個(gè)變量(通過其get 或 set 方法)的每個(gè)線程都有自己的局部變量掏婶,它獨(dú)立于變量的初始化副本。ThreadLocal實(shí)例通常是類中的 private static 字段潭陪,它們希望將狀態(tài)與某一個(gè)線程(例如雄妥,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)。
所以ThreadLocal與線程同步機(jī)制不同依溯,線程同步機(jī)制是多個(gè)線程共享同一個(gè)變量老厌,而ThreadLocal是為每一個(gè)線程創(chuàng)建一個(gè)單獨(dú)的變量副本,故而每個(gè)線程都可以獨(dú)立地改變自己所擁有的變量副本黎炉,而不會(huì)影響其他線程所對(duì)應(yīng)的副本枝秤。可以說(shuō)ThreadLocal為多線程環(huán)境下變量問題提供了另外一種解決思路慷嗜。
鼎鼎大名的線程池淀弹。不需要多說(shuō)!!!!!
這篇博客深入分析 Java 中線程池的實(shí)現(xiàn)。
【死磕Java并發(fā)】—–J.U.C之線程池:ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor 是實(shí)現(xiàn)線程的周期庆械、延遲調(diào)度的薇溃。
ScheduledThreadPoolExecutor,繼承 ThreadPoolExecutor 且實(shí)現(xiàn)了 ScheduledExecutorService 接口缭乘,它就相當(dāng)于提供了“延遲”和“周期執(zhí)行”功能的 ThreadPoolExecutor痊焊。在JDK API中是這樣定義它的:ThreadPoolExecutor,它可另行安排在給定的延遲后運(yùn)行命令,或者定期執(zhí)行命令薄啥。需要多個(gè)輔助線程時(shí)辕羽,或者要求 ThreadPoolExecutor 具有額外的靈活性或功能時(shí),此類要優(yōu)于 Timer垄惧。 一旦啟用已延遲的任務(wù)就執(zhí)行它刁愿,但是有關(guān)何時(shí)啟用,啟用后何時(shí)執(zhí)行則沒有任何實(shí)時(shí)保證到逊。按照提交的先進(jìn)先出 (FIFO) 順序來(lái)啟用那些被安排在同一執(zhí)行時(shí)間的任務(wù)铣口。
來(lái)源:from url