您好,登錄后才能下訂單哦!
java基礎中并發(fā)多線程面試題有哪些,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
線程是操作系統(tǒng)能夠進?運算調(diào)度的最?單位,它被包含在進程之中,是進程中的實際運作單位,可以使?多線程對進?運算提速。
線程安全:
就是多線程訪問時,采?了加鎖機制,當?個線程訪問該類的某個數(shù)據(jù)時,進?保護,其他線程不能進?訪問,直到該線程讀取完,其他線程才可使?。不會出現(xiàn)數(shù)據(jù)不?致或者數(shù)據(jù)污染。Vector 是?同步?法來實現(xiàn)線程安全的, ?和它相似的ArrayList不是線程安全的。
線程不安全:
就是不提供數(shù)據(jù)訪問保護,有可能出現(xiàn)多個線程先后更改數(shù)據(jù)造成所得到的數(shù)據(jù)是臟數(shù)據(jù) 線程安全問題都是由全局變量及靜態(tài)變量引起的。若每個線程中對全局變量、靜態(tài)變量只有讀操作,??寫操作,?般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)?寫操作,?般都需要考慮線程同步,否則的話就可能影響線程安全。
自旋鎖是SMP架構(gòu)中的?種low-level的同步機制。
當線程A想要獲取?把自旋鎖?該鎖?被其它線程鎖持有時,線程A會在?個循環(huán)中自旋以檢測鎖是不是已經(jīng)可?了。
自旋鎖需要注意:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)
由于自旋時不釋放CPU,因?持有自旋鎖的線程應該盡快釋放自旋鎖,否則等待該自旋鎖的線程會?直在那?自旋,這就會浪費CPU時間。
持有自旋鎖的線程在sleep之前應該釋放自旋鎖以便其它線程可以獲得自旋鎖。
?前的JVM實現(xiàn)自旋會消耗CPU,如果?時間不調(diào)?doNotify?法,doWait?發(fā)會?直自旋,CPU會消耗太?。
自旋鎖?較適?于鎖使?者保持鎖時間?較短的情況,這種情況自旋鎖的效率?較?。
自旋鎖是?種對多處理器相當有效的機制,?在單處理器?搶占式的系統(tǒng)中基本上沒有做?。
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)
CAS(compare and swap)的縮寫,中?翻譯成?較并交換。
CAS 不通過JVM,直接利?java本地? JNI(Java Native Interface為JAVA本地調(diào)?),直接調(diào)?CPU 的cmpxchg(是匯編指令)指令。
利?CPU的CAS指令,同時借助JNI來完成Java的?阻塞算法,實現(xiàn)原?操作。其它原?操作都是利?類似的特性完成的。
整個java.util.concurrent都是建?在CAS之上的,因此對于synchronized阻塞算法,J.U.C在性能上有了很?的提升。
CAS是項樂觀鎖技術,當多個線程嘗試使?CAS同時更新同?個變量時,只有其中?個線程能更新變量的值,?其它線程都失敗,失敗的線程并不會被掛起,?是被告知這次競爭中失敗,并可以再次嘗試。
使?CAS在線程沖突嚴重時,會?幅降低程序性能;CAS只適合于線程沖突較少的情況使?。
synchronized在jdk1.6之后,已經(jīng)改進優(yōu)化。synchronized的底層實現(xiàn)主要依靠Lock-Free的隊列,基本思路是自旋后阻塞,競爭切換后繼續(xù)競爭鎖,稍微犧牲了公平性,但獲得了?吞吐量。在線程沖突較少的情況下,可以獲得和CAS類似的性能;?線程沖突嚴重的情況下,性能遠?于CAS。
1.悲觀鎖
Java在JDK1.5之前都是靠synchronized關鍵字保證同步的,這種通過使??致的鎖定協(xié)議來協(xié)調(diào)對共享狀態(tài)的訪問,可以確保?論哪個線程持有共享變量的鎖,都采?獨占的?式來訪問這些變量。獨占鎖其實就是?種悲觀鎖,所以可以說synchronized是悲觀鎖。
2.樂觀鎖
樂觀鎖( Optimistic Locking)其實是?種思想。相對悲觀鎖??,樂觀鎖假設認為數(shù)據(jù)?般情況下不會造成沖突,所以在數(shù)據(jù)進?提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進?檢測,如果發(fā)現(xiàn)沖突了,則讓返回?戶錯誤的信息,讓?戶決定如何去做。memcached使?了cas樂觀鎖技術保證數(shù)據(jù)?致性。
1、AbstractQueuedSynchronizer簡稱AQS,是?個?于構(gòu)建鎖和同步容器的框架。事實上concurrent包內(nèi)許多類都是基于AQS構(gòu)建,例如ReentrantLock,Semaphore,CountDownLatch,ReentrantReadWriteLock,F(xiàn)utureTask等。AQS解決了在實現(xiàn)同步容器時設計的?量細節(jié)問題。
2、AQS使??個FIFO的隊列表示排隊等待鎖的線程,隊列頭節(jié)點稱作“哨兵節(jié)點”或者“啞節(jié)點”,它不與任何線程關聯(lián)。其他的節(jié)點與等待線程關聯(lián),每個節(jié)點維護?個等待狀態(tài)waitStatus。
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)
原?操作是指?個不受其他操作影響的操作任務單元。原?操作是在多線程環(huán)境下避免數(shù)據(jù)不?致必須的?段。
int++并不是?個原?操作,所以當?個線程讀取它的值并加1時,另外?個線程有可能會讀到之前的值,這就會引發(fā)錯誤。
為了解決這個問題,必須保證增加操作是原?的,在JDK1.5之前我們可以使?同步技術來做到這?點。
到JDK1.5,java.util.concurrent.atomic包提供了int和long類型的裝類,它們可以自動的保證對于他們的操作是原?的并且不需要使?同步。
Java通過Executors提供四種線程池,分別為:
newCachedThreadPool創(chuàng)建?個可緩存線程池,如果線程池?度超過處理需要,可靈活回收空閑線程,若?可回收,則新建線程。newFixedThreadPool 創(chuàng)建?個定?線程池,可控制線程最?并發(fā)數(shù),超出的線程會在隊列中等待。newScheduledThreadPool 創(chuàng)建?個定?線程池,?持定時及周期性任務執(zhí)?。newSingleThreadExecutor 創(chuàng)建?個單線程化的線程池,它只會?唯?的?作線程來執(zhí)?任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)?。
1、JDK7提供了7個阻塞隊列。(也屬于并發(fā)容器)
ArrayBlockingQueue :?個由數(shù)組結(jié)構(gòu)組成的有界阻塞隊列。LinkedBlockingQueue :?個由鏈表結(jié)構(gòu)組成的有界阻塞隊列。PriorityBlockingQueue :?個?持優(yōu)先級排序的?界阻塞隊列。DelayQueue:?個使?優(yōu)先級隊列實現(xiàn)的?界阻塞隊列。SynchronousQueue:?個不存儲元素的阻塞隊列。LinkedTransferQueue:?個由鏈表結(jié)構(gòu)組成的?界阻塞隊列。LinkedBlockingDeque:?個由鏈表結(jié)構(gòu)組成的雙向阻塞隊列。
2、概念:阻塞隊列是?個在隊列基礎上??持了兩個附加操作的隊列。
3、2個附加操作:
3.1. ?持阻塞的插??法:隊列滿時,隊列會阻塞插?元素的線程,直到隊列不滿。
3.2 ?持阻塞的移除?法:隊列空時,獲取元素的線程會等待隊列變?yōu)?空。
1、Callable 和 Future 是?較有趣的?對組合。當我們需要獲取線程的執(zhí)?結(jié)果時,就需要?到它們。Callable?于產(chǎn)?結(jié)果,F(xiàn)uture?于獲取結(jié)果。
2、Callable接?使?泛型去定義它的返回類型。Executors類提供了?些有?的?法去在線程池中執(zhí)?Callable內(nèi)的任務。由于Callable任務是并?的,必須等待它返回的結(jié)果。java.util.concurrent.Future對象解決了這個問題。
3、在線程池提交Callable任務后返回了?個Future對象,使?它可以知道Callable任務的狀態(tài)和得到Callable返回的執(zhí)?結(jié)果。Future提供了get()?法,等待Callable結(jié)束并獲取它的執(zhí)?結(jié)果。
1、FutureTask可?于異步獲取執(zhí)?結(jié)果或取消執(zhí)?任務的場景。通過傳?Runnable或者Callable的任務給FutureTask,直接調(diào)?其run?法或者放?線程池執(zhí)?,之后可以在外部通過FutureTask的get?法異步獲取執(zhí)?結(jié)果,因此,F(xiàn)utureTask?常適合?于耗時的計算,主線程可以在完成自?的任務后,再去獲取結(jié)果。另外,F(xiàn)utureTask還可以確保即使調(diào)?了多次run?法,它都只會執(zhí)??次Runnable或者Callable任務,或者通過cancel取消FutureTask的執(zhí)?等。
2、futuretask可?于執(zhí)?多任務、以及避免?并發(fā)情況下多次創(chuàng)建數(shù)據(jù)機鎖的出現(xiàn)。
同步容器:
1、主要代表有Vector和Hashtable,以及Collections.synchronizedXxx等。
2、鎖的粒度為當前對象整體。
3、迭代器是及時失敗的,即在迭代的過程中發(fā)現(xiàn)被修改,就會拋出ConcurrentModificationException。
并發(fā)容器:
1、主要代表有ConcurrentHashMap、CopyOnWriteArrayList、ConcurrentSkipListMap、ConcurrentSkipListSet。
2、鎖的粒度是分散的、細粒度的,即讀和寫是使?不同的鎖。
3、迭代器具有弱?致性,即可以容忍并發(fā)修改,不會拋出ConcurrentModificationException。
ConcurrentHashMap 采?分段鎖技術,同步容器中,是?個容器?個鎖,但在ConcurrentHashMap中,會將hash表的數(shù)組部分分成若?段,每段維護?個鎖,以達到?效的并發(fā)訪問;
1、多線程:是指從軟件或者硬件上實現(xiàn)多個線程的并發(fā)技術。
2、多線程的好處:
使?多線程可以把程序中占據(jù)時間?的任務放到后臺去處理,如圖?、視屏的下載 發(fā)揮多核處理器的優(yōu)勢,并發(fā)執(zhí)?讓系統(tǒng)運?的更快、更流暢,?戶體驗更好
3、多線程的缺點:
?量的線程降低代碼的可讀性;更多的線程需要更多的內(nèi)存空間, 當多個線程對同?個資源出現(xiàn)爭奪時候要注意線程安全的問題。
4、多線程的上下?切換:
CPU通過時間?分配算法來循環(huán)執(zhí)?任務,當前任務執(zhí)??個時間?后會切換到下?個任務。但是,在切換前會保存上?個任務的狀態(tài),以便下次切換回這個任務時,可以再次加載這個任務的狀態(tài)。
Java中的ThreadLocal類允許我們創(chuàng)建只能被同?個線程讀寫的變量。因此,如果?段代碼含有?個ThreadLocal變量的引?,即使兩個線程同時執(zhí)?這段代碼,它們也?法訪問到對?的ThreadLocal變量。
概念:線程局部變量。在并發(fā)編程的時候,成員變量如果不做任何處理其實是線程不安全的,各個線程都在操作同?個變量,顯然是不?的,并且我們也知道volatile這個關鍵字也是不能保證線程安全的。那么在有?種情況之下,我們需要滿?這樣?個條件:變量是同?個,但是每個線程都使?同?個初始值,也就是使?同?個變量的?個新的副本。這種情況之下ThreadLocal就?常適?,?如說DAO的數(shù)據(jù)庫連接,我們知道DAO是單例的,那么他的屬性Connection就不是?個線程安全的變量。?我們每個線程都需要使?他,并且各自使?各自的。這種情況,ThreadLocal就?較好的解決了這個問題。
原理:從本質(zhì)來講,就是每個線程都維護了?個map,?這個map的key就threadLocal,?值就是我們set的那個值,每次線程在get的時候,都從自?的變量中取值,既然從自?的變量中取值,那肯定就不存在線程安全問題,總體來講,ThreadLocal這個變量的狀態(tài)根本沒有發(fā)?變化,他僅僅是充當?個key的??,另外提供給每?個線程?個初始值。
實現(xiàn)機制:每個Thread對象內(nèi)部都維護了?個ThreadLocalMap這樣?個ThreadLocal的Map,可以存放若?個 ThreadLocal。
ThreadPool 優(yōu)點:
減少了創(chuàng)建和銷毀線程的次數(shù),每個?作線程都可以被重復利?,可執(zhí)?多個任務 可以根據(jù)系統(tǒng)的承受能?,調(diào)整線程池中?作線線程的數(shù)?,防?因為因為消耗過多的內(nèi)存,?把服務器累趴下(每個線程需要?約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越?,最后死機)
------減少在創(chuàng)建和銷毀線程上所花的時間以及系統(tǒng)資源的開銷
------如不使?線程池,有可能造成系統(tǒng)創(chuàng)建?量線程?導致消耗完系統(tǒng)內(nèi)存
Java??線程池的頂級接?是Executor,但是嚴格意義上講Executor并不是?個線程池,?只是?個執(zhí)?線程的?具。真正的線程池接?是ExecutorService。
當線程數(shù)?于corePoolSize時,創(chuàng)建線程執(zhí)?任務。
當線程數(shù)?于等于corePoolSize并且workQueue沒有滿時,放?workQueue中
線程數(shù)?于等于corePoolSize并且當workQueue滿時,新任務新建線程運?,線程總數(shù)要?于maximumPoolSize
當線程總數(shù)等于maximumPoolSize并且workQueue滿了的時候執(zhí)?handler的rejectedExecution。也就是拒絕策略。
1、ArrayBlockingQueue 數(shù)組結(jié)構(gòu)組成的有界阻塞隊列。
2、CountDownLatch 允許?個或多個線程等待其他線程完成操作;join?于讓當前執(zhí)?線程等待join線程執(zhí)?結(jié)束。其實現(xiàn)原理是不停檢查join線程是否存活,如果join線程存活則讓當前線程永遠wait。
基礎知識:
1.可重?鎖??芍?鎖是指同?個線程可以多次獲取同?把鎖。ReentrantLocksynchronized都是可重?鎖。
2.可中斷鎖??芍袛噫i是指線程嘗試獲取鎖的過程中,是否可以響應中斷。synchronized是不可中斷鎖,?ReentrantLock則提供了中斷功能。公平鎖與?公平鎖。公平鎖是指多個線程同時嘗試獲取同?把鎖時,獲取鎖的順序按照線程達到的順序,??公平鎖則允許線程“插隊”。synchronized是?公平鎖,?ReentrantLock的默認實現(xiàn)是?公平鎖,但是也可以設置為公平鎖。
3.CAS操作(CompareAndSwap)。CAS操作簡單的說就是?較并交換。CAS 操作包含三個操作數(shù) —— 內(nèi)存位置(V)、預期原值(A)和新值(B)。如果內(nèi)存位置的值與預期原值相匹配,那么處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。?論哪種情況,它都會在 CAS 指令之前返回該位置的值。CAS 有效地說明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現(xiàn)在的值即可。”
4.Synchronized: isynchronized是java內(nèi)置的關鍵字,它提供了?種獨占的加鎖?式。synchronized的獲取和釋放鎖由JVM實現(xiàn),?戶不需要顯示的釋放鎖,?常?便。然?synchronized也有?定的局限性:
當線程嘗試獲取鎖的時候,如果獲取不到鎖會?直阻塞。
如果獲取鎖的線程進?休眠或者阻塞,除?當前線程異常,否則其他線程嘗試獲取鎖必須?直等待。
5.ReentrantLock:
ReentrantLock它是JDK 1.5之后提供的API層?的互斥鎖,需要lock()和unlock()?法配合try/finally語句塊來完成。
等待可中斷避免,出現(xiàn)死鎖的情況(如果別的線程正持有鎖,會等待參數(shù)給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false)
公平鎖與?公平鎖多個線程等待同?個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖?公平鎖,ReentrantLock默認的構(gòu)造函數(shù)是創(chuàng)建的?公平鎖,可以通過參數(shù)true設為公平鎖,但公平鎖表現(xiàn)的性能不是很好。
Semaphore就是?個信號量,它的作?是限制某段代碼塊的并發(fā)數(shù)
1、Lock接??同步?法和同步塊提供了更具擴展性的鎖操作。他們允許更靈活的結(jié)構(gòu),可以具有完全不同的性質(zhì),并且可以?持多個相關類的條件對象。
2、它的優(yōu)勢有:
可以使鎖更公平 可以使線程在等待鎖的時候響應中斷 可以讓線程嘗試獲取鎖,并在?法獲取鎖的時候?即返回或者等待?段時間 可以在不同的范圍,以不同的順序獲取和釋放鎖
1、同?時間只能有?條線程執(zhí)?固定類的同步?法,但是對于類的?同步?法,可以多條線程同時訪問。所以,這樣就有問題了,可能線程A在執(zhí)?Hashtable的put?法添加數(shù)據(jù),線程B則可以正常調(diào)?size()?法讀取Hashtable中當前元素的個數(shù),那讀取到的值可能不是最新的,可能線程A添加了完了數(shù)據(jù),但是沒有對size++,線程B就已經(jīng)讀取size了,那 么對于線程B來說讀取到的size?定是不準確的。
2、?給size()?法加了同步之后,意味著線程B調(diào)?size()?法只有在線程A調(diào)?put?法完畢之后才可以調(diào)?,這樣就保證了線程安全性。
1、?作機制(分?思想):它引?了?個“分段鎖”的概念,具體可以理解為把?個?的Map拆分成N個?的segment,根據(jù)key.hashCode()來決定把key放到哪個HashTable中??梢蕴峁┫嗤木€程安全,但是效率提升N倍,默認提升16倍。
2、應?:當讀>寫時使?,適合做緩存,在程序啟動時初始化,之后可以被多個線程訪問;
3、hash沖突:
簡介:HashMap中調(diào)?hashCode()?法來計算hashCode。由于在Java中兩個不同的對象可能有?樣的hashCode,所以不同的鍵可能有?樣hashCode,從?導致沖突的產(chǎn)?。hash沖突解決:使?平衡樹來代替鏈表,當同?hash中的元素數(shù)量超過特定的值便會由鏈表切換到平衡樹
4、?鎖讀:ConcurrentHashMap之所以有較好的并發(fā)性是因為ConcurrentHashMap是?鎖讀和加鎖寫,并且利?了分段鎖(不是在所有的entry上加鎖,?是在?部分entry上加鎖);
讀之前會先判斷count(jdk1.6),其中的count是被volatile修飾的(當變量被volatile修飾后,每次更改該變量的時候會將更改結(jié)果寫到系統(tǒng)主內(nèi)存中,利?多處理器的緩存?致性,其他處理器會發(fā)現(xiàn)自?的緩存?對應的內(nèi)存地址被修改,就會將自?處理器的緩存?設置為失效,并強制從系統(tǒng)主內(nèi)存獲取最新的數(shù)據(jù)。),故可以實現(xiàn)?鎖讀。
5、ConcurrentHashMap的并發(fā)度就是segment的??,默認為16,這意味著最多同時可以有16條線程操作ConcurrentHashMap,這也是ConcurrentHashMap對Hashtable的最?優(yōu)勢。
1、讀寫鎖:分為讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由jvm自?控制的,你只要上好相應的鎖即可。
2、如果你的代碼只讀數(shù)據(jù),可以很多?同時讀,但不能同時寫,那就上讀鎖;
3、如果你的代碼修改數(shù)據(jù),只能有?個?在寫,且不能同時讀取,那就上寫鎖??傊x的時候上讀鎖,寫的時候上寫鎖!
CyclicBarrier和CountDownLatch 都位于java.util.concurrent 這個包下
LockSupport是JDK中?較底層的類,?來創(chuàng)建鎖和其他同步?具類的基本線程阻塞。java鎖和同步器框架的核? AQS:AbstractQueuedSynchronizer,就是通過調(diào)? LockSupport .park()和 LockSupport .unpark()實現(xiàn)線程的阻塞和喚醒的。
在java.util.concurrent包中,有兩個很特殊的?具類,Condition和ReentrantLock,使?過的?都知道,ReentrantLock(重?鎖)是jdk的concurrent包提供的?種獨占鎖的實現(xiàn)。
我們知道在線程的同步時可以使?個線程阻塞?等待?個信號,同時放棄鎖使其他線程可以能競爭到鎖。
在synchronized中我們可以使?Object的wait()和notify?法實現(xiàn)這種等待和喚醒。
但是在Lock中怎么實現(xiàn)這種wait和notify呢?答案是Condition,學習Condition主要是為了?便以后學習blockqueue和concurrenthashmap的源碼,同時也進?步理解ReentrantLock。
1、Fork就是把?個?任務切分為若??任務并?的執(zhí)?。
2、Join就是合并這些?任務的執(zhí)?結(jié)果,最后得到這個?任務的結(jié)果。
1、sleep() ?法是線程類(Thread)的靜態(tài)?法,讓調(diào)?線程進?睡眠狀態(tài),讓出執(zhí)?機會給其他線程,等到休眠時間結(jié)束后,線程進?就緒狀態(tài)和其他線程?起競爭cpu的執(zhí)?時間。
因為sleep() 是static靜態(tài)的?法,他不能改變對象的機鎖,當?個synchronized塊中調(diào)?了sleep() ?法,線程雖然進?休眠,但是對象的機鎖沒有被釋放,其他線程依然?法訪問這個對象。
2、wait() wait()是Object類的?法,當?個線程執(zhí)?到wait?法時,它就進?到?個和該對象相關的等待池,同時釋放對象的機鎖,使得其他線程能夠訪問,可以通過notify,notifyAll?法來喚醒等待的線程。
線程通常都有五種狀態(tài),創(chuàng)建、就緒、運?、阻塞和死亡。
第?是創(chuàng)建狀態(tài)。在?成線程對象,并沒有調(diào)?該對象的start?法,這是線程處于創(chuàng)建狀態(tài)。
第?是就緒狀態(tài)。當調(diào)?了線程對象的start?法之后,該線程就進?了就緒狀態(tài),但是此時線程調(diào)度程序還沒有把該線程設置為當前線程,此時處于就緒狀態(tài)。在線程運?之后,從等待或者睡眠中回來之后,也會處于就緒狀態(tài) 。第三是運?狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設置為當前線程,此時線程就進?了運?狀態(tài),開始運?run函數(shù)當中的代碼。
第四是阻塞狀態(tài)。線程正在運?的時候,被暫停,通常是為了等待某個時間的發(fā)?(?如說某項資源就緒)之后再繼續(xù)運?。sleep,suspend,wait等?法都可以導致線程阻塞。
第五是死亡狀態(tài)。如果?個線程的run?法執(zhí)?結(jié)束或者調(diào)?stop?法后,該線程就會死亡。對于已經(jīng)死亡的線程,?法再使?start?法令其進?就緒。
1、start()?法來啟動?個線程,真正實現(xiàn)了多線程運?。
2、如果直接調(diào)?run(),其實就相當于是調(diào)?了?個普通函數(shù)?已,直接調(diào)?run()?法必須等待run()?法執(zhí)?完畢才能執(zhí)?下?的代碼,所以執(zhí)?路徑還是只有?條,根本就沒有線程的特征,所以在多線程執(zhí)?時要使?start()?法?不是run()?法。
Runnable接?中的run()?法的返回值是void,它做的事情只是純粹地去執(zhí)?run()?法中的代碼?已;
Callable接?中的call()?法是有返回值的,是?個泛型,和Future、FutureTask配合可以?來獲取異步執(zhí)?的結(jié)果。
多線程主要圍繞可?性和原?性兩個特性?展開,使?volatile關鍵字修飾的變量,保證了其在多線程之間的可?性,即每次讀取到volatile變量,?定是最新的數(shù)據(jù)。
代碼底層執(zhí)?不像我們看到的?級語?—-Java程序這么簡單,它的執(zhí)?是Java代碼–>字節(jié)碼–>根據(jù)字節(jié)碼執(zhí)?對應的C/C++代碼–>C/C++代碼被編譯成匯編語?–>和硬件電路交互,現(xiàn)實中,為了獲取更好的性能JVM可能會對指令進?重排序,多線程下可能會出現(xiàn)?些意想不到的問題。使?volatile則會對禁?語義重排序,當然這也?定程度上降低了代碼執(zhí)?效率。
死循環(huán)、死鎖、阻塞、??打開慢等問題,查看線程dump是最好的解決問題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:
獲取到線程的pid,可以通過使?jps命令,在Linux環(huán)境下還可以使?ps -ef | grep java
打印線程堆棧,可以通過使?jstack pid命令,在Linux環(huán)境下還可以使?kill -3 pid
另外提?點,Thread類提供了?個getStackTrace()?法也可以?于獲取線程堆棧。這是?個實例?法,因此此?法是和具體線程實例綁定的,每次獲取到的是具體某個線程當前運?的堆棧。
進程是系統(tǒng)進?資源分配的基本單位,有獨?的內(nèi)存地址空間
線程是CPU獨?運?和獨?調(diào)度的基本單位,沒有單獨地址空間,有獨?的棧,局部變量,寄存器, 程序計數(shù)器等。
創(chuàng)建進程的開銷?,包括創(chuàng)建虛擬地址空間等需要?量系統(tǒng)資源
創(chuàng)建線程開銷?,基本上只有?個內(nèi)核對象和?個堆棧。
?個進程?法直接訪問另?個進程的資源;同?進程內(nèi)的多個線程共享進程的資源。
進程切換開銷?,線程切換開銷?;進程間通信開銷?,線程間通信開銷?。
線程屬于進程,不能獨?執(zhí)?。每個進程?少要有?個線程,成為主線程
繼承Thread類,重寫run?法
實現(xiàn)Runnable接?,重寫run?法,實現(xiàn)Runnable接?的實現(xiàn)類的實例對象作為Thread構(gòu)造函數(shù)的target
實現(xiàn)Callable接?通過FutureTask包裝器來創(chuàng)建Thread線程
通過線程池創(chuàng)建線程
高并發(fā)、任務執(zhí)?時間短的業(yè)務:線程池線程數(shù)可以設置為CPU核數(shù)+1,減少線程上下?的切換。
并發(fā)不?、任務執(zhí)?時間?的業(yè)務要區(qū)分開看:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術社區(qū)
假如是業(yè)務時間?集中在IO操作上,也就是IO密集型的任務,因為IO操作并不占?CPU,所以不要讓所有的CPU閑下來,可以加?線程池中的線程數(shù)?,讓CPU處理更多的業(yè)務
假如是業(yè)務時間?集中在計算操作上,也就是計算密集型任務,這個就沒辦法了,和(1)?樣吧,線程池中的線程數(shù)設置得少?些,減少線程上下?的切換
并發(fā)?、業(yè)務執(zhí)?時間?,解決這種類型任務的關鍵不在于線程池?在于整體架構(gòu)的設計,看看這些業(yè)務??某些數(shù)據(jù)是否能做緩存是第?步,增加服務器是第?步,?于線程池的設置,設置參考(2)。最后,業(yè)務執(zhí)?時間?的問題, 也可能需要分析?下,看看能不能使?中間件對任務進?拆分和解耦。
1、如果你使?的LinkedBlockingQueue,也就是?界隊列的話,沒關系,繼續(xù)添加任務到阻塞隊列中等待執(zhí)?,因為LinkedBlockingQueue可以近乎認為是?個?窮?的隊列,可以?限存放任務;
2、如果你使?的是有界隊列??說ArrayBlockingQueue的話,任務?先會被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,則會使?拒絕策略RejectedExecutionHandler處理滿了的任務,默認是AbortPolicy。
?法鎖(synchronized修飾?法時):
通過在?法聲明中加? synchronized關鍵字來聲明 synchronized ?法。
synchronized ?法控制對類成員變量的訪問;
每個類實例對應?把鎖,每個 synchronized ?法都必須獲得調(diào)?該?法的類實例的鎖?能執(zhí)?,否則所屬線程阻塞,?法?旦執(zhí)?,就獨占該鎖,直到從該?法返回時才將鎖釋放,此后被阻塞的線程?能獲得該鎖,重新進?可執(zhí)?狀態(tài)。這種機制確保了同?時刻對于每?個類實例,其所有聲明為 synchronized 的成員函數(shù)中?多只有?個處于可執(zhí)?狀態(tài),從?有效避免了類成員變量的訪問沖突。
對象鎖(synchronized修飾?法或代碼塊):
當?個對象中有synchronized method或synchronized block的時候調(diào)?此對象的同步?法或進?其同步區(qū)域時,就必須先獲得對象鎖。如果此對象的對象鎖已被其他調(diào)?者占?,則需要等待此鎖被釋放。(?法鎖也是對象鎖)
java的所有對象都含有1個互斥鎖,這個鎖由JVM自動獲取和釋放。線程進?synchronized?法的時候獲取該對象的鎖,當然如果已經(jīng)有線程獲取了這個對象的鎖,那么當前線程會等待;synchronized?法正常返回或者拋異常?終?,JVM會自動釋放對象鎖。這?也體現(xiàn)了?synchronized來加鎖的1個好處,?法拋異常的時候,鎖仍然可以由JVM來自動釋放。
類鎖(synchronized 修飾靜態(tài)的?法或代碼塊):
由于?個class不論被實例化多少次,其中的靜態(tài)?法和靜態(tài)變量在內(nèi)存中都只有?份。所以,?旦?個靜態(tài)的?法被申明為synchronized。此類所有的實例化對象在調(diào)?此?法,共?同?把鎖,我們稱之為類鎖。
對象鎖是?來控制實例?法之間的同步,類鎖是?來控制靜態(tài)?法(或靜態(tài)變量互斥體)之間的同步
synchronized?法正常返回或者拋異常?終?,JVM會自動釋放對象鎖
解釋?:并?是指兩個或者多個事件在同?時刻發(fā)?;?并發(fā)是指兩個或多個事件在同?時間間隔發(fā)?。
解釋?:并?是在不同實體上的多個事件,并發(fā)是在同?實體上的多個事件。
解釋三:在?臺處理器上“同時”處理多個任務,在多臺處理器上同時處理多個任務。如hadoop分布式集群所以并發(fā)編程的?標是充分的利?處理器的每?個核,以達到最?的處理性能。
volatile只能保證你數(shù)據(jù)的可?性,獲取到的是最新的數(shù)據(jù),不能保證原?性;
?AtomicInteger保證原?性。
synchronized既能保證共享變量可?性,也可以保證鎖內(nèi)操作的原?性。
如果這個異常沒有被捕獲的話,這個線程就停?執(zhí)?了。
另外重要的?點是:如果這個線程持有某個對象的監(jiān)視器,那么這個對象監(jiān)視器會被?即釋放.
通過在線程之間共享對象就可以了,然后通過wait/notify/notifyAll、await/signal/signalAll進?喚起和等待,??說阻塞隊列BlockingQueue就是為線程之間共享數(shù)據(jù)?設計的。
通過平衡?產(chǎn)者的?產(chǎn)能?和消費者的消費能?來提升整個系統(tǒng)的運?效率,這是?產(chǎn)者消費者模型最重要的作?。
解耦,這是?產(chǎn)者消費者模型附帶的作?,解耦意味著?產(chǎn)者和消費者之間的聯(lián)系少,聯(lián)系越少越可以獨自發(fā)展?不需要受到相互的制約。
如果線程是因為調(diào)?了wait()、sleep()或者join()?法?導致的阻塞;
suspend與resume:Java廢棄 suspend() 去掛起線程的原因,是因為 suspend() 在導致線程暫停的同時,并不會去釋放任何鎖資源。其他線程都?法訪問被它占?的鎖。直到對應的線程執(zhí)? resume() ?法后,被掛起的線程才能繼續(xù),從?其它被阻塞在這個鎖的線程才可以繼續(xù)執(zhí)?。但是,如果 resume() 操作出現(xiàn)在 suspend() 之前執(zhí)?,那么線程將?直處于掛起狀態(tài),同時?直占?鎖,這就產(chǎn)?了死鎖。?且,對于被掛起的線程,它的線程狀態(tài)居然還是 Runnable。
wait與notify:wait與notify必須配合synchronized使?,因為調(diào)?之前必須持有鎖,wait會?即釋放鎖,notify則是同步塊執(zhí)?完了才釋放
await與singal:Condition類提供,?Condition對象由new ReentLock().newCondition()獲得,與wait和notify相同,因為使?Lock鎖后?法使?wait?法
park與unpark:LockSupport是?個?常?便實?的線程阻塞?具,它可以在線程任意位置讓線程阻塞。和Thread.suspenf()相?,它彌補了由于resume()在前發(fā)?,導致線程?法繼續(xù)執(zhí)?的情況。和Object.wait()相?,它不需要先獲得某個對象的鎖,也不會拋出IException異常??梢詥拘阎付ň€程。如果線程遇到了IO阻塞,?能為?,因為IO是操作系統(tǒng)實現(xiàn)的,Java代碼并沒有辦法直接接觸到操作系統(tǒng)。
搶占式。?個線程?完CPU之后,操作系統(tǒng)會根據(jù)線程優(yōu)先級、線程饑餓情況等數(shù)據(jù)算出?個總的優(yōu)先級并分配下?個時間?給某個線程執(zhí)?。
??常談的問題了,?先要說的是單例模式的線程安全意味著:某個類的實例在多線程環(huán)境下只會被創(chuàng)建?次出來。單例模式有很多種的寫法,我總結(jié)?下:
(1)餓漢式單例模式的寫法:線程安全
(2)懶漢式單例模式的寫法:?線程安全
(3)雙檢鎖單例模式的寫法:線程安全
線程類的構(gòu)造?法、靜態(tài)塊是被new這個線程類所在的線程所調(diào)?的,?run?法??的代碼才是被線程自身所調(diào)?的。
同步塊是更好的選擇,因為它不會鎖住整個對象(當然也可以讓它鎖住整個對象)。同步?法會鎖住整個對象,哪怕這個類中有多個不相關聯(lián)的同步塊,這通常會導致他們停?執(zhí)?并需要等待獲得這個對象上的鎖。
synchronized(this)以及?static的synchronized?法(?于static synchronized?法請往下看),只能防?多個線程同時執(zhí)?同?個對象的同步代碼段。
如果要鎖住多個對象?法,可以鎖住?個固定的對象,或者鎖住這個類的Class對象。
synchronized鎖住的是括號?的對象,?不是代碼。對于?static的synchronized?法,鎖的就是對象本身也就是this。
概念:是指兩個或兩個以上的進程在執(zhí)?過程中,因爭奪資源?造成的?種互相等待的現(xiàn)象,若?外?作?,它們都將?法推進下去。此時稱系統(tǒng)處于死鎖;
死鎖的四個必要條件:
互斥條件:進程對所分配到的資源不允許其他進程進?訪問,若其他進程訪問該資源,只能等待,直?占有該資源的進程使?完成后釋放該資源
請求和保持條件:進程獲得?定的資源之后,?對其他資源發(fā)出請求,但是該資源可能被其他進程占有,此時請求阻塞,但?對自?獲得的資源保持不放
不可剝奪條件:是指進程已獲得的資源,在未完成使?之前,不可被剝奪,只能在使?完后自?釋放
環(huán)路等待條件:是指進程發(fā)?死鎖后,若?進程之間形成?種頭尾相接的循環(huán)等待資源關系
死鎖產(chǎn)?的原因:
因競爭資源發(fā)?死鎖 現(xiàn)象:系統(tǒng)中供多個進程共享的資源的數(shù)?不?以滿?全部進程的需要時,就會引起對諸資源的競爭?發(fā)?死鎖現(xiàn)象
進程推進順序不當發(fā)?死鎖
檢查死鎖:
有兩個容器,?個?于保存線程正在請求的鎖,?個?于保存線程已經(jīng)持有的鎖。每次加鎖之前都會做如下檢測
檢測當前正在請求的鎖是否已經(jīng)被其它線程持有,如果有,則把那些線程找出來
遍歷第?步中返回的線程,檢查自?持有的鎖是否正被其中任何?個線程請求,如果第?步返回真,表示出現(xiàn)了死鎖
死鎖的解除與預防:
控制不要讓四個必要條件成?。
要注意死循環(huán)的問題,HashMap的put操作引發(fā)擴容,這個動作在多線程并發(fā)下會發(fā)?線程死循環(huán)的問題。
1、HashMap不是線程安全的;Hashtable線程安全,但效率低,因為是Hashtable是使?synchronized的,所有線程競爭同?把鎖;?ConcurrentHashMap不僅線程安全?且效率?,因為它包含?個segment數(shù)組,將數(shù)據(jù)分段存儲,給每?段數(shù)據(jù)配?把鎖,也就是所謂的鎖分段技術。
2、HashMap為何線程不安全:
put時key相同導致其中?個線程的value被覆蓋;
多個線程同時擴容,造成數(shù)據(jù)丟失;
多線程擴容時導致Node鏈表形成環(huán)形結(jié)構(gòu)造成.next()死循環(huán),導致CPU利?率接近100%;
3、ConcurrentHashMap最?效;
守護線程(即daemon thread),是個服務線程,準確地來說就是服務其他的線程,這是它的作?——?其他的線程只有?種,那就是?戶線程。所以java?線程分2種,
1、守護線程,?如垃圾回收線程,就是最典型的守護線程。
2、?戶線程,就是應?程序?的自定義線程。
a. 為了控制線程執(zhí)?的順序,如ThreadA->ThreadB->ThreadC->ThreadA循環(huán)執(zhí)?三個線程,我們需要確定喚醒、等待的順序。這時我們可以同時使? Obj.wait()、Obj.notify()與synchronized(Obj)來實現(xiàn)這個?標。
線程中持有上?個線程類的對象鎖以及自?的鎖,由于這種依賴關系,該線程執(zhí)?需要等待上個對象釋放鎖,從? 保證類線程執(zhí)?的順序。
b. 通常情況下,wait是線程在獲取對象鎖后,主動釋放對象鎖,同時本線程休眠,直到有其它線程調(diào)?對象的notify()喚醒該線程,才能繼續(xù)獲取對象鎖,并繼續(xù)執(zhí)?。?notify()則是對等待對象鎖的線程的喚醒操作。但值得注意的是notify()調(diào)?后,并不是?上就釋放對象鎖,?是在相應的synchronized(){}語句塊執(zhí)?結(jié)束。釋放對象鎖后,JVM會在執(zhí)?wait()等待對象鎖的線程中隨機選取?線程,賦予其對象鎖,喚醒線程,繼續(xù)執(zhí)?。
a. 不可以,線程有5種狀態(tài),新建(new)、可運?(runnable)、運?中(running)、阻塞(block)、死亡(dead)。
b. 只有當線程run?法或者主線程main?法結(jié)束,?或者拋出異常時,線程才會結(jié)束?命周期。
在某個對象的所有synchronized?法中,在某個時刻只能有?個唯?的?個線程去訪問這些synchronized?法
如果?個?法是synchronized?法,那么該synchronized關鍵字表示給當前對象上鎖(即this)相當于synchronized(this){}
如果?個synchronized?法是static的,那么該synchronized表示給當前對象所對應的class對象上鎖(每個類不管?成多少對象,其對應的class對象只有?個)
基本原理:??個狀態(tài)值表示鎖,對鎖的占?和釋放通過狀態(tài)值來標識。
三種分布式鎖:
第一種:Zookeeper:
基于zookeeper瞬時有序節(jié)點實現(xiàn)的分布式鎖,其主要邏輯如下。?致思想即為:每個客戶端對某個功能加鎖時,在zookeeper上的與該功能對應的指定節(jié)點的?錄下,?成?個唯?的瞬時有序節(jié)點。判斷是否獲取鎖的?式很簡單,只需要判斷有序節(jié)點中序號最?的?個。當釋放鎖的時候,只需將這個瞬時節(jié)點刪除即可。同時,其可以避免服務宕機導致的鎖?法釋放,?產(chǎn)?的死鎖問題
【優(yōu)點】鎖安全性?,zk可持久化,且能實時監(jiān)聽獲取鎖的客戶端狀態(tài)。?旦客戶端宕機,則瞬時節(jié)點隨之消失,zk因?能第?時間釋放鎖。這也省去了?分布式緩存實現(xiàn)鎖的過程中需要加?超時時間判斷的這?邏輯。
【缺點】性能開銷?較?。因為其需要動態(tài)產(chǎn)?、銷毀瞬時節(jié)點來實現(xiàn)鎖功能。所以不太適合直接提供給?并發(fā)的場景使?。
【實現(xiàn)】可以直接采?zookeeper第三?庫curator即可?便地實現(xiàn)分布式鎖。
【適?場景】對可靠性要求?常?,且并發(fā)程度不?的場景下使?。如核?數(shù)據(jù)的定時全量/增量同步等。)
第二種memcached:
memcached帶有add函數(shù),利?add函數(shù)的特性即可實現(xiàn)分布式鎖。add和set的區(qū)別在于:如果多線程并發(fā)set,則每個set都會成功,但最后存儲的值以最后的set的線程為準。?add的話則相反,add會添加第?個到達的值,并返回true,后續(xù)的添加則都會返回false。利?該點即可很輕松地實現(xiàn)分布式鎖。
【優(yōu)點】并發(fā)?效
【缺點】 memcached采?列?LRU置換策略,所以如果內(nèi)存不夠,可能導致緩存中的鎖信息丟失。memcached?法持久化,?旦重啟,將導致信息丟失。
【使?場景】?并發(fā)場景。需要 1)加上超時時間避免死鎖; 2)提供?夠?撐鎖服務的內(nèi)存空間; 3)穩(wěn)定的集群化管理。
第三種Redis:
redis分布式鎖即可以結(jié)合zk分布式鎖鎖?度安全和memcached并發(fā)場景下效率很好的優(yōu)點,其實現(xiàn)?式和memcached類似,采?setnx即可實現(xiàn)。需要注意的是,這?的redis也需要設置超時時間,以避免死鎖??梢岳?jedis客戶端實現(xiàn)。
數(shù)據(jù)庫死鎖機制和解決?案:
死鎖:死鎖是指兩個或者兩個以上的事務在執(zhí)?過程中,因爭奪鎖資源?造成的?種互相等待的現(xiàn)象。
處理機制:解決死鎖最有?最簡單的?法是不要有等待,將任何等待都轉(zhuǎn)化為回滾,并且事務重新開始。但是有可能影響并發(fā)性能。
------超時回滾,innodb_lock_wait_time設置超時時間;
------wait-for-graph?法:跟超時回滾?起來,這是?種更加主動的死鎖檢測?式。InnoDB引擎也采?這種?式。
1、ThreadLocal:spring使?ThreadLocal解決線程安全問題;ThreadLocal會為每?個線程提供?個獨?的變量副本,從?隔離了多個線程對數(shù)據(jù)的訪問沖突。因為每?個線程都擁有自?的變量副本,從?也就沒有必要對該變量進?同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。概括起來說,對于多線程資源共享的問題,同步機制采?了“以時間換空間”的?式,?ThreadLocal采?了“以空間換時間”的?式。前者僅提供?份變量,讓不同的線程排隊訪問,?后者為每?個線程都提供了?份變量,因此可以同時訪問?互不影響。在很多情況下,ThreadLocal?直接使?synchronized同步機制解決線程安全問題更簡單,更?便,且結(jié)果程序擁有更?的并發(fā)性。
2、單例:?狀態(tài)的Bean(?狀態(tài)就是?次操作,不能保存數(shù)據(jù)。?狀態(tài)對象(Stateless Bean),就是沒有實例變量的對象,不能保存數(shù)據(jù),是不變類,是線程安全的。)適合?不變模式,技術就是單例模式,這樣可以共享實例,提?性能。
使?場景:假設?個服務器完成?項任務所需時間為:T1-創(chuàng)建線程時間,T2-在線程中執(zhí)?任務的時間,T3-銷毀線程時間。如果T1+T3遠?于T2,則可以使?線程池,以提?服務器性能;
組成:
線程池管理器(ThreadPool):?于創(chuàng)建并管理線程池,包括 創(chuàng)建線程池,銷毀線程池,添加新任務;
?作線程(PoolWorker):線程池中線程,在沒有任務時處于等待狀態(tài),可以循環(huán)的執(zhí)?任務;
任務接?(Task):每個任務必須實現(xiàn)的接?,以供?作線程調(diào)度任務的執(zhí)?,它主要規(guī)定了任務的??,任務執(zhí)?完后的收尾?作,任務的執(zhí)?狀態(tài)等;
任務隊列(taskQueue):?于存放沒有處理的任務。提供?種緩沖機制。
原理:線程池技術正是關注如何縮短或調(diào)整T1,T3時間的技術,從?提?服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結(jié)束的時間段或者?些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。
?作流程:
1、線程池剛創(chuàng)建時,??沒有?個線程(也可以設置參數(shù)prestartAllCoreThreads啟動預期數(shù)量主線程)。任務隊列是作為參數(shù)傳進來的。不過,就算隊列??有任務,線程池也不會?上執(zhí)?它們。
2、當調(diào)? execute() ?法添加?個任務時,線程池會做如下判斷:
如果正在運?的線程數(shù)量?于 corePoolSize,那么?上創(chuàng)建線程運?這個任務;
如果正在運?的線程數(shù)量?于或等于 corePoolSize,那么將這個任務放?隊列;
如果這時候隊列滿了,?且正在運?的線程數(shù)量?于 maximumPoolSize,那么還是要創(chuàng)建?核?線程?刻運?這個任務;
如果隊列滿了,?且正在運?的線程數(shù)量?于或等于 maximumPoolSize,那么線程池會拋出異常RejectExecutionException。
3、當?個線程完成任務時,它會從隊列中取下?個任務來執(zhí)?。
4、當?個線程?事可做,超過?定的時間(keepAliveTime)時,線程池會判斷,如果當前運?的線程數(shù)?于corePoolSize,那么這個線程就被停掉。所以線程池的所有任務完成后,它最終會收縮到 corePoolSize 的??。
例如:在銀?系統(tǒng)轉(zhuǎn)賬時,需要鎖定兩個賬戶,這個時候,順序使?兩個synchronized可能存在死鎖的情況
1、繼承Thread類;
2、實現(xiàn)Runnable接?;
3、直接在函數(shù)體內(nèi):
?較:
1、實現(xiàn)Runnable接?優(yōu)勢:
1)適合多個相同的程序代碼的線程去處理同?個資源
2)可以避免java中的單繼承的限制
3)增加程序的健壯性,代碼可以被多個線程共享,代碼和數(shù)據(jù)獨?。
2、繼承Thread類優(yōu)勢:
1)可以將線程類抽象出來,當需要使?抽象??模式設計時。
2)多線程同步
3、在函數(shù)體使?優(yōu)勢
1)?需繼承thread或者實現(xiàn)Runnable,縮?作?域。
1、java中有兩種鎖:?種是?法鎖或者對象鎖(在?靜態(tài)?法或者代碼塊上加鎖),第?種是類鎖(在靜態(tài)?法或者class上加鎖);
2、注意:其他線程可以訪問未加鎖的?法和代碼;synchronized同時修飾靜態(tài)?法和實例?法,但是運?結(jié)果是交替進?的,這證明了類鎖和對象鎖是兩個不?樣的鎖,控制著不同的區(qū)域,它們是互不?擾的。
1、使?消息隊列,消息持久化;
2、添加標志位:未處理 0,處理中 1,已處理 2。定時處理。
1、OOM實現(xiàn):
ThreadLocal的實現(xiàn)是這樣的:每個Thread 維護?個 ThreadLocalMap 映射表,這個映射表的 key 是 ThreadLocal實例本身,value 是真正需要存儲的 Object。2、也就是說 ThreadLocal 本身并不存儲值,它只是作為?個 key 來讓線程從 ThreadLocalMap 獲取 value。值得注意的是圖中的虛線,表示 ThreadLocalMap 是使? ThreadLocal 的弱引?作為 Key 的,弱引?的對象在 GC 時會被回收。
ThreadLocalMap使?ThreadLocal的弱引?作為key,如果?個ThreadLocal沒有外部強引?來引?它,那么系統(tǒng) GC的時候,這個ThreadLocal勢必會被回收,這樣?來,ThreadLocalMap中就會出現(xiàn)key為null的Entry,就沒有辦法訪問這些key為null的Entry的value,如果當前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會?直存在?條強引?鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠?法回收,造成內(nèi)存泄漏。
3、預防辦法:在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap?所有key為null的value。但是這些被動的預防措施并不能保證不會內(nèi)存泄漏:
(1)使?static的ThreadLocal,延?了ThreadLocal的?命周期,可能導致內(nèi)存泄漏。
(2)分配使?了ThreadLocal?不再調(diào)?get(),set(),remove()?法,那么就會導致內(nèi)存泄漏,因為這塊內(nèi)存?直存在。
Java 7為實現(xiàn)并?訪問,引?了Segment這?結(jié)構(gòu),實現(xiàn)了分段鎖,理論上最?并發(fā)度與Segment個數(shù)相等。
Java 8為進?步提?并發(fā)性,摒棄了分段鎖的?案,?是直接使??個?的數(shù)組。同時為了提?哈希碰撞下的尋址性能,Java 8在鏈表?度超過?定閾值(8)時將鏈表(尋址時間復雜度為O(N))轉(zhuǎn)換為紅?樹(尋址時間復雜度為O(long(N)))。
ConcurrentHashMap、Future、FutureTask、AtomicInteger…
1、CountDownLatch類
?個同步輔助類,常?于某個條件發(fā)?后才能執(zhí)?后續(xù)進程。給定計數(shù)初始化CountDownLatch,調(diào)?countDown()?法,在計數(shù)到達零之前,await?法?直受阻塞。
重要?法為countdown()與await();
2、join?法
將線程B加?到線程A的尾部,當A執(zhí)?完后B才執(zhí)?。
3、notify、wait?法,Java中的喚醒與等待?法,關鍵為synchronized代碼塊,參數(shù)線程間應相同,也常?Object作為參數(shù)。
1、?并發(fā)系統(tǒng)性能優(yōu)化:優(yōu)化程序,優(yōu)化服務配置,優(yōu)化系統(tǒng)配置
盡量使?緩存,包括?戶緩存,信息緩存等,多花點內(nèi)存來做緩存,可以?量減少與數(shù)據(jù)庫的交互,提?性能。
?jprofiler等?具找出性能瓶頸,減少額外的開銷。
優(yōu)化數(shù)據(jù)庫查詢語句,減少直接使?hibernate等?具的直接?成語句(僅耗時較?的查詢做優(yōu)化)。
優(yōu)化數(shù)據(jù)庫結(jié)構(gòu),多做索引,提?查詢效率。
統(tǒng)計的功能盡量做緩存,或按每天?統(tǒng)計或定時統(tǒng)計相關報表,避免需要時進?統(tǒng)計的功能。
能使?靜態(tài)??的地?盡量使?,減少容器的解析(盡量將動態(tài)內(nèi)容?成靜態(tài)html來顯示)。
解決以上問題后,使?服務器集群來解決單臺的瓶頸問題。
2.防?庫存超賣:
悲觀鎖:在更新庫存期間加鎖,不允許其它線程修改;
數(shù)據(jù)庫鎖:select xxx for update;
分布式鎖;
樂觀鎖:使?帶版本號的更新。每個線程都可以并發(fā)修改,但在并發(fā)時,只有?個線程會修改成功,其它會返回失敗。
redis watch:監(jiān)視鍵值對,作?時如果事務提交exec時發(fā)現(xiàn)監(jiān)視的監(jiān)視對發(fā)?變化,事務將被取消。
消息隊列:通過 FIFO 隊列,使修改庫存的操作串?化。
總的來說,不能把壓?放在數(shù)據(jù)庫上,所以使? “select xxx for update” 的?式在?并發(fā)的場景下是不可?的。FIFO 同步隊列的?式,可以結(jié)合庫存限制隊列?,但是在庫存較多的場景下,?不太適?。所以相對來說,我會傾向于選擇:樂觀鎖 / 緩存鎖 / 分布式鎖的?式。
看完上述內(nèi)容,你們掌握java基礎中并發(fā)多線程面試題有哪些的方法了嗎?如果還想學到更多技能或想了解更多相關內(nèi)容,歡迎關注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。