線程知識補(bǔ)全(大廠必問)

ThreadLocal定義

線程本地變量,也有些地方叫做線程本地存儲,其實(shí)意思差不多。ThreadLocal可以讓每個(gè)線程擁有一個(gè)屬于自己的變量的副本,不會和其他線程的變量副本沖突,實(shí)現(xiàn)了線程的數(shù)據(jù)隔離。

threadlocal如何實(shí)現(xiàn)的?

每個(gè)Thread線程內(nèi)部都有一個(gè)Map。

Map里面存儲線程本地對象線程(key)和線程的變量副本(value)

但是,Thread內(nèi)部的Map是由ThreadLocal維護(hù)的,由ThreadLocal負(fù)責(zé)向map獲取和設(shè)置線程的變量值。

get()方法用于獲取當(dāng)前線程的副本變量值。

set()方法用于保存當(dāng)前線程的副本變量值。

initialValue()為當(dāng)前線程初始副本變量值。

remove()方法移除當(dāng)前前程的副本變量值。

1.獲取當(dāng)前線程的ThreadLocalMap對象threadLocals

2.從map中獲取線程存儲的K-V Entry節(jié)點(diǎn)。

3.從Entry節(jié)點(diǎn)獲取存儲的Value副本值返回。

4.map為空的話返回初始值null,即線程變量副本為null,在使用時(shí)需要注意判斷NullPointerException。

每個(gè)線程只存一個(gè)變量,這樣的話所有的線程存放到map中的Key都是相同的ThreadLocal,如果一個(gè)線程要保存多個(gè)變量,就需要?jiǎng)?chuàng)建多個(gè)ThreadLocal,多個(gè)ThreadLocal放入Map中時(shí)會極大的增加Hash沖突的可能。

ThreadLocalMap

ThreadLocalMap是ThreadLocal的內(nèi)部類,沒有實(shí)現(xiàn)Map接口,用獨(dú)立的方式實(shí)現(xiàn)了Map的功能,其內(nèi)部的Entry也獨(dú)立實(shí)現(xiàn)。在ThreadLocalMap中,也是用Entry來保存K-V結(jié)構(gòu)數(shù)據(jù)的。但是Entry中key只能是ThreadLocal對象,這點(diǎn)被Entry的構(gòu)造方法已經(jīng)限定死了。

由于ThreadLocalMap的key是弱引用,而Value是強(qiáng)引用。這就導(dǎo)致了一個(gè)問題,ThreadLocal在沒有外部對象強(qiáng)引用時(shí),發(fā)生GC時(shí)弱引用Key會被回收,而Value不會回收,如果創(chuàng)建ThreadLocal的線程一直持續(xù)運(yùn)行,那么這個(gè)Entry對象中的value就有可能一直得不到回收,發(fā)生內(nèi)存泄露。

既然Key是弱引用,那么我們要做的事,就是在調(diào)用ThreadLocal的get()、set()方法時(shí)完成后再調(diào)用remove方法,將Entry節(jié)點(diǎn)和Map的引用關(guān)系移除,這樣整個(gè)Entry對象在GC Roots分析后就變成不可達(dá)了,下次GC的時(shí)候就可以被回收。如果使用ThreadLocal的set方法之后,沒有顯示的調(diào)用remove方法,就有可能發(fā)生內(nèi)存泄露,所以養(yǎng)成良好的編程習(xí)慣十分重要,使用完ThreadLocal之后,記得調(diào)用remove方法。

CAS的基本原理(CAS的無鎖編程原理)?

CAS加volatile關(guān)鍵字是實(shí)現(xiàn)并發(fā)包的基石。沒有CAS就不會有并發(fā)包,synchronized是一種獨(dú)占鎖、悲觀鎖,java.util.concurrent中借助了CAS指令實(shí)現(xiàn)了一種區(qū)別于synchronized的一種樂觀鎖。因?yàn)閠1和t2線程都同時(shí)去訪問同一變量56,所以他們會把主內(nèi)存的值完全拷貝一份到自己的工作內(nèi)存空間,所以t1和t2線程的預(yù)期值都為56。假設(shè)t1在與t2線程競爭中線程t1能去更新變量的值,而其他線程都失敗。(失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次發(fā)起嘗試)。t1線程去更新變量值改為57,然后寫到內(nèi)存中。此時(shí)對于t2來說,內(nèi)存值變?yōu)榱?7,與預(yù)期值56不一致,就操作失敗了(想改的值不再是原來的值)。

阻塞隊(duì)列

阻塞隊(duì)列(BlockingQueue)是一個(gè)支持兩個(gè)附加操作的隊(duì)列。這兩個(gè)附加的操作是:在隊(duì)列為空時(shí),獲取元素的線程(取數(shù)據(jù))會等待隊(duì)列變?yōu)榉强?。?dāng)隊(duì)列滿時(shí),存儲元素的線程(填數(shù)據(jù))會等待隊(duì)列可用。阻塞隊(duì)列常用于生產(chǎn)者和消費(fèi)者的場景,生產(chǎn)者是往隊(duì)列里添加元素的線程,消費(fèi)者是從隊(duì)列里拿元素的線程。阻塞隊(duì)列就是生產(chǎn)者存放元素的容器,而消費(fèi)者也只從容器里拿元素。

什么是線程池?為什么要用線程池?

1、什么是線程池:? java.util.concurrent.Executors提供了一個(gè) java.util.concurrent.Executor接口的實(shí)現(xiàn)用于創(chuàng)建線程池多線程技術(shù)主要解決處理器單元內(nèi)多個(gè)線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時(shí)間,增加處理器單元的吞吐能力。假設(shè)一個(gè)服務(wù)器完成一項(xiàng)任務(wù)所需時(shí)間為:T1 創(chuàng)建線程時(shí)間,T2 在線程中執(zhí)行任務(wù)的時(shí)間,T3 銷毀線程時(shí)間。如果:T1 + T3 遠(yuǎn)大于 T2,則可以采用線程池,以提高服務(wù)器性能。

一個(gè)線程池包括以下四個(gè)基本組成部分:

1、線程池管理器(ThreadPool):用于創(chuàng)建并管理線程池,包括 創(chuàng)建線程池,銷毀線程池,添加新任務(wù);

2、工作線程(PoolWorker):線程池中線程,在沒有任務(wù)時(shí)處于等待狀態(tài),可以循環(huán)的執(zhí)行任務(wù);

3、任務(wù)接口(Task):每個(gè)任務(wù)必須實(shí)現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行,它主要規(guī)定了任務(wù)的入口,任務(wù)執(zhí)行完后的收尾工作,任務(wù)的執(zhí)行狀態(tài)等;

4、任務(wù)隊(duì)列(taskQueue):用于存放沒有處理的任務(wù)。提供一種緩沖機(jī)制。

線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時(shí)間的技術(shù),從而提高服務(wù)器程序性能的。它把T1,T3分別安排在服務(wù)器程序的啟動和結(jié)束的時(shí)間段或者一些空閑的時(shí)間段,這樣在服務(wù)器程序處理客戶請求時(shí),不會有T1,T3的開銷了。線程池不僅調(diào)整T1,T3產(chǎn)生的時(shí)間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目,看一個(gè)例子:假設(shè)一個(gè)服務(wù)器一天要處理50000個(gè)請求,并且每個(gè)請求需要一個(gè)單獨(dú)的線程完成。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會超過線程池中線程的數(shù)目,而如果服務(wù)器不利用線程池來處理這些請求則線程總數(shù)為50000。一般線程池大小是遠(yuǎn)小于50000。所以利用線程池的服務(wù)器程序不會為了創(chuàng)建50000而在處理請求時(shí)浪費(fèi)時(shí)間,從而提高效率。

2.常見線程池

①newSingleThreadExecutor

單個(gè)線程的線程池,即線程池中每次只有一個(gè)線程工作,單線程串行執(zhí)行任務(wù)

②newFixedThreadExecutor(n)

固定數(shù)量的線程池,沒提交一個(gè)任務(wù)就是一個(gè)線程,直到達(dá)到線程池的最大數(shù)量,然后后面進(jìn)入等待隊(duì)列直到前面的任務(wù)完成才繼續(xù)執(zhí)行

③newCacheThreadExecutor(推薦使用)

可緩存線程池,當(dāng)線程池大小超過了處理任務(wù)所需的線程,那么就會回收部分空閑(一般是60秒無執(zhí)行)的線程,當(dāng)有任務(wù)來時(shí),又智能的添加新線程來執(zhí)行。

④newScheduleThreadExecutor

大小無限制的線程池,支持定時(shí)和周期性的執(zhí)行線程

AQS(顯示鎖)?

AQS使用方式和其中的設(shè)計(jì)模式

繼承,模板方法設(shè)計(jì)模式可以實(shí)現(xiàn)自己的同步工具類.用一個(gè)int類型的state保存同步狀態(tài)。內(nèi)部本質(zhì)上是CLH隊(duì)列鎖,每一個(gè)需要拿鎖的線程打包一個(gè)節(jié)點(diǎn)掛到鏈表上。每一個(gè)線程檢測前一個(gè)節(jié)點(diǎn)是否釋放鎖,如果釋放就可以拿到鎖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容