北京百子灣后現(xiàn)代城網(wǎng)站建設(shè)商業(yè)網(wǎng)站
ThreadLocal
ThreadLocal 有什么用?通常情況下,我們創(chuàng)建的變量是可以被任何一個線程訪問并修改的。如果想實現(xiàn)每一個線程都有自己的專屬本地變量該如何解決呢?JDK 中自帶的ThreadLocal類正是為了解決這樣的問題。 ThreadLocal類主要解決的就是讓每個線程綁定自己的值,可以將ThreadLocal類形象的比喻成存放數(shù)據(jù)的盒子,盒子中可以存儲每個線程的私有數(shù)據(jù)。如果你創(chuàng)建了一個ThreadLocal變量,那么訪問這個變量的每個線程都會有這個變量的本地副本,這也是ThreadLocal變量名的由來。他們可以使用 get() 和 set() 方法來獲取默認(rèn)值或?qū)⑵渲蹈臑楫?dāng)前線程所存的副本的值,從而避免了線程安全問題。
再舉個簡單的例子:兩個人去寶屋收集寶物,這兩個共用一個袋子的話肯定會產(chǎn)生爭執(zhí),但是給他們兩個人每個人分配一個袋子的話就不會出現(xiàn)這樣的問題。如果把這兩個人比作線程的話,那么 ThreadLocal 就是用來避免這兩個線程競爭的。
線程池
池化技術(shù)想必大家已經(jīng)屢見不鮮了,線程池、數(shù)據(jù)庫連接池、Http 連接池等等都是對這個思想的應(yīng)用。池化技術(shù)的思想主要是為了減少每次獲取資源的消耗,提高對資源的利用率。線程池提供了一種限制和管理資源(包括執(zhí)行一個任務(wù))的方式。 每個線程池還維護(hù)一些基本統(tǒng)計信息,例如已完成任務(wù)的數(shù)量。
使用線程池的好處:
降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
如何創(chuàng)建線程池?
方式一:通過ThreadPoolExecutor構(gòu)造函數(shù)來創(chuàng)建(推薦)。
線程池常見的參數(shù)有哪些
ThreadPoolExecutor 3 個最重要的參數(shù):
corePoolSize : 任務(wù)隊列未達(dá)到隊列容量時,最大可以同時運行的線程數(shù)量。
maximumPoolSize : 任務(wù)隊列中存放的任務(wù)達(dá)到隊列容量的時候,當(dāng)前可以同時運行的線程數(shù)量變?yōu)樽畲缶€程數(shù)。
workQueue: 新任務(wù)來的時候會先判斷當(dāng)前運行的線程數(shù)量是否達(dá)到核心線程數(shù),如果達(dá)到的話,新任務(wù)就會被存放在隊列中。ThreadPoolExecutor其他常見參數(shù) :
keepAliveTime:線程池中的線程數(shù)量大于 corePoolSize 的時候,如果這時沒有新的任務(wù)提交,核心線程外的線程不會立即銷毀,而是會等待,直到等待的時間超過了 keepAliveTime才會被回收銷毀;
unit : keepAliveTime 參數(shù)的時間單位。
threadFactory :executor 創(chuàng)建新線程的時候會用到。
handler :飽和策略。關(guān)于飽和策略下面單獨介紹一下。
線程池處理任務(wù)的流程
如果當(dāng)前運行的線程數(shù)小于核心線程數(shù),那么就會新建一個線程來執(zhí)行任務(wù)。如果當(dāng)前運行的線程數(shù)等于或大于核心線程數(shù),但是小于最大線程數(shù),那么就把該任務(wù)放入到任務(wù)隊列里等待執(zhí)行。如果向任務(wù)隊列投放任務(wù)失敗(任務(wù)隊列已經(jīng)滿了),但是當(dāng)前運行的線程數(shù)是小于最大線程數(shù)的,就新建一個線程來執(zhí)行任務(wù)。如果當(dāng)前運行的線程數(shù)已經(jīng)等同于最大線程數(shù)了,新建線程將會使當(dāng)前運行的線程超出最大線程數(shù),那么當(dāng)前任務(wù)會被拒絕,飽和策略會調(diào)用RejectedExecutionHandler.rejectedExecution()方法。# 如何給線程池命名?
Future
Future 類是異步思想的典型運用,主要用在一些需要執(zhí)行耗時任務(wù)的場景,避免程序一直原地等待耗時任務(wù)執(zhí)行完成,執(zhí)行效率太低。具體來說是這樣的:當(dāng)我們執(zhí)行某一耗時的任務(wù)時,可以將這個耗時任務(wù)交給一個子線程去異步執(zhí)行,同時我們可以干點其他事情,不用傻傻等待耗時任務(wù)執(zhí)行完成。等我們的事情干完后,我們再通過 Future 類獲取到耗時任務(wù)的執(zhí)行結(jié)果。這樣一來,程序的執(zhí)行效率就明顯提高了。這其實就是多線程中經(jīng)典的 Future 模式,你可以將其看作是一種設(shè)計模式,核心思想是異步調(diào)用,主要用在多線程領(lǐng)域,并非 Java 語言獨有。
在 Java 中,Future 類只是一個泛型接口,位于 java.util.concurrent 包下,其中定義了 5 個方法,主要包括下面這 4 個功能:
取消任務(wù);
判斷任務(wù)是否被取消;
判斷任務(wù)是否已經(jīng)執(zhí)行完成;
獲取任務(wù)執(zhí)行結(jié)果。
AQS
AQS 的全稱為 AbstractQueuedSynchronizer ,翻譯過來的意思就是抽象隊列同步器。這個類在 java.util.concurrent.locks 包下面。
AQS 的原理是什么?
AQS 核心思想是,如果被請求的共享資源空閑,則將當(dāng)前請求資源的線程設(shè)置為有效的工作線程,并且將共享資源設(shè)置為鎖定狀態(tài)。如果被請求的共享資源被占用,那么就需要一套線程阻塞等待以及被喚醒時鎖分配的機(jī)制,這個機(jī)制 AQS 是用 CLH 隊列鎖實現(xiàn)的,即將暫時獲取不到鎖的線程加入到隊列中。
CLH(Craig,Landin,and Hagersten) 隊列是一個虛擬的雙向隊列(虛擬的雙向隊列即不存在隊列實例,僅存在結(jié)點之間的關(guān)聯(lián)關(guān)系)。AQS 是將每條請求共享資源的線程封裝成一個 CLH 鎖隊列的一個結(jié)點(Node)來實現(xiàn)鎖的分配。在 CLH 同步隊列中,一個節(jié)點表示一個線程,它保存著線程的引用(thread)、 當(dāng)前節(jié)點在隊列中的狀態(tài)(waitStatus)、前驅(qū)節(jié)點(prev)、后繼節(jié)點(next)。