Java并发编程艺术读书笔记
1、多線程在CPU切換過(guò)程中,由于需要保存線程之前狀態(tài)和加載新線程狀態(tài),成為上下文切換,上下文切換會(huì)造成消耗系統(tǒng)內(nèi)存。所以,可合理控制線程數(shù)量。 如何控制:
(1)使用ps -ef|grep appname,查找appname的pid;如1111
(2)使用jstack 1111 > /home/ibethfy/dump1,將dump信息追加到dump1
(3)使用grep java.lang.Thread.State /home/ibethfy/dump1 | awk '{print $2$3$4$5}' | sort | uniq -c,統(tǒng)計(jì)查找線程狀態(tài)個(gè)數(shù)。分析是否WAITING的線程過(guò)多。
(4)查看這些WAITING的線程時(shí)怎么產(chǎn)生的,如某個(gè)服務(wù)創(chuàng)建線程池中線程過(guò)多,分析后對(duì)應(yīng)修改。
(5)重啟后重新統(tǒng)計(jì)。
2、避免死鎖幾種常用方式
(1)避免一個(gè)線程同時(shí)獲取多個(gè)鎖。
(2)避免一個(gè)線程在鎖內(nèi)同時(shí)占用多個(gè)資源,盡量保證每個(gè)鎖只占用一個(gè)資源。
(3)嘗試使用定時(shí)鎖,lock.tryLock(timeout)來(lái)替代內(nèi)部鎖機(jī)制。
(4)對(duì)于數(shù)據(jù)庫(kù)鎖,加鎖和解鎖必須在同一個(gè)連接內(nèi),不然會(huì)造成解鎖失敗的情況。
3、volatile保證共享資源對(duì)其他線程的可見(jiàn)性。volatile修飾共享變量時(shí),共享變量進(jìn)行寫操作時(shí),會(huì)在匯編中多一個(gè)lock前綴的指令,該指定表示要將該變量從當(dāng)前處理器的緩存行數(shù)據(jù)寫到系統(tǒng)內(nèi)存,寫會(huì)內(nèi)存操作會(huì)使其他CPU緩存了該內(nèi)存地址的數(shù)據(jù)失效。其他CPU去獲取該數(shù)據(jù)時(shí),會(huì)從系統(tǒng)內(nèi)存重新獲取。
4、synchronized,JVM通過(guò)進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)代碼塊和方法同步。代碼塊同步使用的是monitorenter和monitorexit來(lái)實(shí)現(xiàn)。在編譯時(shí),會(huì)將這兩個(gè)指令分別插入到代碼塊開(kāi)始和結(jié)束(包括正常和異常結(jié)束),在執(zhí)行這兩句指令時(shí),會(huì)去進(jìn)去和退出monitor對(duì)象。
5、鎖在哪?Java的鎖,是存在對(duì)象頭的Mark Word中。存儲(chǔ)一個(gè)鎖地址,指向C++里面的一個(gè)ObjectMonitor對(duì)象,該對(duì)象存儲(chǔ)了當(dāng)前占用鎖的線程,等待的線程,該線程重入的次數(shù),鎖狀態(tài)等字段。
6、JMM,Java內(nèi)存模型,用于控制Java線程之間的通信,和計(jì)算機(jī)多線程模型類似,也是為每個(gè)線程分配了類似的內(nèi)存緩存,變量存在主內(nèi)存中,要保證線程之間的通信,必須要保證線程A將數(shù)據(jù)從本地緩存刷新到主內(nèi)存,線程B識(shí)別到數(shù)據(jù)更新,從主內(nèi)存中取得最新數(shù)據(jù)。JMM主要就是控制主內(nèi)存和線程本地內(nèi)存之間的交互等。
7、JMM模型中,主要是解決有序性(順序一致性),可見(jiàn)性,原子性。
(1)由于多線程中,每個(gè)線程有自己的本地緩存,且在執(zhí)行代碼后,不知道何時(shí)回把緩存刷新到主內(nèi)存,所以,線程的操作對(duì)其他線程是不可見(jiàn)的。這時(shí)不同線程獲取共享變量就會(huì)出現(xiàn)不一致的問(wèn)題。
(2)指令的重排序:重排序是指編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段。數(shù)據(jù)依賴性:某一行代碼依賴上一行代碼,這樣不會(huì)進(jìn)行這兩條代碼的重排序,多線程系統(tǒng)不會(huì)考慮該問(wèn)題。順序一致性:指令重排序后,最終執(zhí)行結(jié)果要和順序執(zhí)行結(jié)果一致。
(3)多線程之間進(jìn)行了正確的數(shù)據(jù)同步,JMM保證多個(gè)線程的順序一致性。
(4)順序一致性模型:一個(gè)程序的操作必須按照程序順序執(zhí)行。不管程序是否同步,所有線程都能按照單一的操作執(zhí)行順序。即多個(gè)線程的操作可以并行執(zhí)行,但對(duì)于單個(gè)線程來(lái)看,他的指令時(shí)順序執(zhí)行的,且每條指令都對(duì)其他線程可見(jiàn)。
(5)JMM不保證順序一致性,即未同步的線程,執(zhí)行順序是無(wú)序的,且可能對(duì)其他內(nèi)存不可見(jiàn)。這個(gè)時(shí)候,就需要同步來(lái)實(shí)現(xiàn)順序一致性模型。synchronized修飾的代碼塊,在獲得鎖后才能執(zhí)行,從而保證了多個(gè)線程的整體順序執(zhí)行,但同步塊局部不依賴的指令,也可進(jìn)行重排序(盡量給編譯器或者優(yōu)化器優(yōu)化提供入口)。為什么JMM不保證未同步的線程的順序一致性呢?主要是要保證會(huì)禁止大量的編譯器等優(yōu)化,嚴(yán)重影響性能,所以就提供同步方法來(lái)實(shí)現(xiàn)。
(6)JMM內(nèi)存可見(jiàn)性的保證:單線程不會(huì)出現(xiàn)內(nèi)存可見(jiàn)性問(wèn)題,編譯器,runtime,處理器會(huì)共同保證期執(zhí)行結(jié)果和順序一致性模型結(jié)果一致;多線程,正確同步的多線程將具有順序一致性,JMM通過(guò)限制編譯器和處理器的重排序來(lái)提供內(nèi)存可見(jiàn)性的保證。未同步或未正確同步的多線程,JMM為他們提供了最小安全性的保證,線程讀取到的值,要么是之前線程寫入的值,要么是初始值。
(7)volatile,當(dāng)寫volatile時(shí),其他讀寫指令在其之后執(zhí)行,保證了有序性;當(dāng)寫時(shí),會(huì)把新的緩存中的值寫入到主內(nèi)存;另一線程讀時(shí),會(huì)將本地緩存地址置為無(wú)效,從主內(nèi)存中獲取該值。
(8)synchronized,當(dāng)某線程獲得鎖時(shí),其余線程等待,保證了有序性。當(dāng)某線程釋放鎖時(shí),會(huì)將本地緩存寫入主內(nèi)存,當(dāng)另一線程獲取鎖時(shí),會(huì)將本地緩存置為無(wú)效,從主內(nèi)存獲取數(shù)據(jù)。所以保證了多線程的順序一致性和可見(jiàn)性。
(9)happens-before規(guī)則。
8、wait和notify:WaitThread首先獲取了對(duì)象的鎖,然后調(diào)用鎖對(duì)象的wait()方法,從而放棄了鎖并進(jìn)入了對(duì)象的等待隊(duì)列WaitQueue中,進(jìn)入等待狀態(tài)。由于WaitThread釋放了對(duì)象的鎖,NotifyThread隨后獲取了對(duì)象的鎖,并調(diào)用鎖對(duì)象的notify()方法,將WaitThread從WaitQueue移到SynchronizedQueue中,此時(shí)WaitThread的狀態(tài)變?yōu)樽枞麪顟B(tài)。NotifyThread釋放了鎖之后,WaitThread再次獲取到鎖并從wait()方法返回繼續(xù)執(zhí)行。金典案例如下: synchronized(對(duì)象)?{ ???????
while(條件不滿足)?{ ?????????????
?對(duì)象.wait(); ?????
??} ?????
? ?對(duì)應(yīng)的處理邏輯
}
synchronized(對(duì)象)?{
???????改變條件 ???????
對(duì)象.notifyAll();
}
9、ThreadLocal:ThreadLocal,即線程變量,是一個(gè)以ThreadLocal線程對(duì)象為鍵、任意對(duì)象為值的存儲(chǔ)結(jié)構(gòu)。這個(gè)結(jié)構(gòu)被附帶在線程上,也就是說(shuō)一個(gè)線程可以根據(jù)一個(gè)ThreadLocal對(duì)象查詢到綁定在這個(gè)線程上的一個(gè)值。
10、CountDownLatch:
(1)、初始化CountDownLatch cdl? = new CountDownLatch(5);
(2)、同一線程或多個(gè)線程調(diào)用cdl.countDown(),使計(jì)數(shù)器減一,通過(guò)入?yún)魅隿dl;等待線程調(diào)用cdl.await(),阻塞等待cdl計(jì)數(shù)器等于0后,執(zhí)行。
11、CyclicBarrier:
(1)、初始化CyclicBarrier cb = new CyclicBarrier();
(2)、其余線程調(diào)用cb.await(),告訴屏障我已到達(dá),然后等待指定數(shù)量的線程都已到達(dá)后,然后統(tǒng)一往后執(zhí)行。
(3)、構(gòu)造函數(shù)可傳入另一線程,表示到達(dá)屏障的數(shù)量滿足后,優(yōu)先執(zhí)行該方法。
12、Semaphore:Semaphore(10)表示允許10個(gè)線程獲取許可證,也就是最大并發(fā)數(shù)是10。Semaphore的用法也很簡(jiǎn)單,首先線程使用Semaphore的acquire()方法獲取一個(gè)許可證,使用完之后調(diào)用release()方法歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證。
13、Exchanger:用于線程間數(shù)據(jù)交換。
14、線程池技術(shù):好處
(1)降低系統(tǒng)消耗,重復(fù)利用線程
(2)提高響應(yīng)速度,不用重新創(chuàng)建線程
(3)提高可管理性,可以實(shí)現(xiàn)對(duì)線程的管理。
線程池流程:
(1)新來(lái)一個(gè)任務(wù),如果沒(méi)有超過(guò)核心線程數(shù),則創(chuàng)建一個(gè)核心線程,執(zhí)行任務(wù),不管是否有閑置的核心線程,也會(huì)創(chuàng)建新的核心線程執(zhí)行任務(wù)。
(2)如果核心線程都處于運(yùn)行狀態(tài),且數(shù)量已達(dá)上限,則判斷工作隊(duì)列是否已滿,如果未滿,則將任務(wù)加入工作隊(duì)列,排隊(duì)等待核心線程空閑后執(zhí)行。
(3)如果核心線程都在運(yùn)行任務(wù)和工作隊(duì)列都滿了,則創(chuàng)建新的額外線程來(lái)執(zhí)行任務(wù)。(額外線程由最大線程決定,且無(wú)用后會(huì)被回收)
(4)額外線程也滿了且都在執(zhí)行的話,就采用拒絕策略拒絕請(qǐng)求。
線程池創(chuàng)建參數(shù):
(1)corePoolSize,核心線程數(shù)量。
(2)runnableTaskQueue:任務(wù)隊(duì)列,有ArrayBlockingQueue,基于數(shù)組的有界阻塞隊(duì)列,FIFO;LinkedBlockingQueue,基于鏈表實(shí)現(xiàn)的阻塞隊(duì)列,FIFO,吞吐量通常高于ArrayBlockingQueue,靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列;SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于Linked-BlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列;
(3)maximumPoolSize:線程池最大線程數(shù),線程池允許創(chuàng)建的最大線程數(shù)。如果隊(duì)列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是,如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒(méi)什么效果。
(4)ThreadFactory:用于設(shè)置創(chuàng)建線程的工廠;
(5)RejectedExecutionHandler(飽和策略):當(dāng)隊(duì)列和線程池都滿了,說(shuō)明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常。以下四種策略: ·AbortPolicy:直接拋出異常。 ·CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)。 ·DiscardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。 ·DiscardPolicy:不處理,丟棄掉
(6)keepAliveTime(線程活動(dòng)保持時(shí)間):線程池的工作線程空閑后(非核心線程會(huì)回收,allowCoreThreadTimeOut(true),設(shè)置這個(gè)屬性,核心線程在超時(shí)未用后,也會(huì)被回收),保持存活的時(shí)間。所以,如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短,可以調(diào)大時(shí)間,提高線程的利用率。
(7)TimeUnit(線程活動(dòng)保持時(shí)間的單位);
轉(zhuǎn)載于:https://www.cnblogs.com/ibethfy/p/10119329.html
總結(jié)
以上是生活随笔為你收集整理的Java并发编程艺术读书笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 梦到单位发东西怎么回事
- 下一篇: 冒泡排序(Java版)