帶你快速搞定java多線程(3)
一、鎖的概念
先來聊聊這幾個概念,總不能聊起來的時候啥也不知道,只知道干活也沒有用。
公平鎖:當線程A獲取訪問該對象,獲取到鎖后,此時內(nèi)部存在一個計數(shù)器num+1,其他線程想訪問該對象,就會進行排隊等待(等待隊列最前一個線程處于待喚醒狀態(tài)),直到線程A釋放鎖(num = 0),此時會喚醒處于待喚醒狀態(tài)的線程進行獲取鎖的操作,一直循環(huán)。如果線程A再次嘗試獲取該對象鎖時,會檢查該對象鎖釋放已經(jīng)被占用,如果還是當前線程占用鎖,則直接獲得鎖,不用進入排隊。
非公平鎖:當線程A在釋放鎖后,等待對象的線程會進行資源競爭,競爭成功的線程將獲取該鎖,其他線程繼續(xù)睡眠。
公平鎖是嚴格的以FIFO的方式進行鎖的競爭,但是非公平鎖是無序的鎖競爭,剛釋放鎖的線程很大程度上能比較快的獲取到鎖,隊列中的線程只能等待,所以非公平鎖可能會有“饑餓”的問題。但是重復的鎖獲取能減小線程之間的切換,而公平鎖則是嚴格的線程切換,這樣對操作系統(tǒng)的影響是比較大的,所以非公平鎖的吞吐量是大于公平鎖的,這也是為什么JDK將非公平鎖作為默認的實現(xiàn)。
悲觀鎖:總是假設(shè)最壞的情況,每次想要使用數(shù)據(jù)的時候就恰好別人也要修改數(shù)據(jù),一切是以安全第一,所以在每次操作資源的時候都會先加鎖,不管有沒有人搶,然后獨占資源。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現(xiàn)
樂觀鎖:樂觀鎖和悲觀鎖剛好相反,假定自己使用資源的時候沒有人搶,所以不需要上鎖。樂觀鎖的實現(xiàn)方案一般來說有兩種:版本號機制 和 CAS實現(xiàn) 。下期可能會講。
在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現(xiàn)方式CAS實現(xiàn)的。
二、synchronized 的使用方式
場景 | 具體分類 | 鎖對象 | 代碼示例 |
---|---|---|---|
修飾方法 | 實例方法 | 當前實例對象 | public synchronized void method () { ... } |
... | 靜態(tài)方法 | 當前類的Class對象 | public static synchronized void method () { ... } |
修飾代碼塊 | 代碼塊 | ( )中配置的對象 | synchronized(object) { ... } |
三、synchronized 的實現(xiàn)原理列
想知道原來先去底層看下,看看字節(jié)碼是什么樣子的,let's go!
private static Object lock = new Object(); public static synchronized void testSyn() { System.out.println("香菜"); } public synchronized void testSyn2() { System.out.println("香菜"); } public static void testObj() { synchronized (lock) { System.out.println("香菜"); } }
看下字節(jié)碼:
可以看到synchronized 的地方使用的是monitorenter指令,每個對象都和一個monitor對象關(guān)聯(lián),主要用來控制互斥資源的訪問,如果你想要加鎖必須先獲得monitor的批準,如果現(xiàn)在正有線程訪問,會把申請的線程加入到等待隊列。
小結(jié)
1、 無論synchronized關(guān)鍵字加在方法上還是對象上,如果它作用的對象是非靜態(tài)的,則它取得的鎖是對象;如果synchronized作用的對象是一個靜態(tài)方法或一個類,則它取得的鎖是對class對象的鎖,該類所有的對象同一把鎖。2、每個對象只有一個鎖(lock)與之相關(guān)聯(lián),誰拿到這個鎖誰就可以運行它所控制的那段代碼。
3、實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制,避免做嵌套synchronized 的使用。
4、synchronized 要盡量控制范圍,不能范圍太大,否則會損失系統(tǒng)性能。
四、線程池是什么
線程池就是一個對象持有一堆線程,舉個例子就是餓了么養(yǎng)的騎手團隊。線程池就是這個團隊,每個騎手都是一個線程。
五、為什么要用線程池?
假如現(xiàn)在商家有外賣單子,需要騎手去送單,這個時候的外賣任務就會派單給騎手,為什么要用線程池吶?
有幾個好處,第一就是騎手的招聘是有成本的,等你有了外賣訂單再去招聘,來不及了,不如平常養(yǎng)一些騎手,線程的創(chuàng)建和銷毀的開銷是巨大的。
第二就是不能一個單子來了就來一個騎手,這樣的話騎手的數(shù)量很難控制,對于派單來說也存在很大的壓力,會造成整個騎手團隊的崩潰,對應的就是可以通過線程池控制系統(tǒng)內(nèi)的線程數(shù)量,有效的避免大量的線程池爭奪CPU資源而造成堵塞。
第三如果養(yǎng)了一個騎手團隊,這樣在騎手的管理上可以規(guī)范,以便提供更好的外賣服務,比如這種外賣超時,騎手打星等。對比線程池就是線程池可以提供定時、定期、單線程、并發(fā)數(shù)控制等功能。
六、看下類圖,從整體上理解下
七、線程池的創(chuàng)建
線程池主要使用的四種
固定數(shù)量的線程池(FixedThreadPool
)
定時線程池(ScheduledThreadPool
)
可緩存線程池(CachedThreadPool
)
單線程化線程池(SingleThreadExecutor
)
八、線程池核心參數(shù)說明
首先看下如何構(gòu)造一個線程池
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
核心參數(shù)說明:
九、幾個疑問點
9.1、是怎么保證線程不銷毀的?
核心線程會阻塞等待workQueue
9.2 提交任務有哪幾種方式?
9.3 拒絕策略都有哪些?
拒絕策略(handler)當線程池的線程數(shù)達到最大線程數(shù)時,需要執(zhí)行拒絕策略。拒絕策略需要實現(xiàn)RejectedExecutionHandler接口,并實現(xiàn)rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法。不過Executors框架已經(jīng)為我們實現(xiàn)了4種拒絕策略:
AbortPolicy
(默認):丟棄任務并拋出RejectedExecutionException異常。
CallerRunsPolicy
:由調(diào)用線程處理該任務。
DiscardPolicy
:丟棄任務,但是不拋出異常。可以配合這種模式進行自定義的處理方式。
DiscardOldestPolicy
:丟棄隊列最早的未處理任務,然后重新嘗試執(zhí)行任務。
9.4 線程池的關(guān)閉
關(guān)閉線程池可以調(diào)用shutdownNow和shutdown兩個方法來實現(xiàn)
shutdownNow
:對正在執(zhí)行的任務全部發(fā)出interrupt(),停止執(zhí)行,對還未開始執(zhí)行的任務全部取消,并且返回還沒開始的任務列表。
shutdown
:當我們調(diào)用shutdown后,線程池將不再接受新的任務,但也不會去強制終止已經(jīng)提交或者正在執(zhí)行中的任務。
9.5 初始化線程池時線程數(shù)的選擇
如果任務是IO密集型,一般線程數(shù)需要設(shè)置2倍CPU數(shù)以上,以此來盡量利用CPU資源。
如果任務是CPU密集型,一般線程數(shù)量只需要設(shè)置CPU數(shù)加1即可,更多的線程數(shù)也只能增加上下文切換,不能增加CPU利用率。
具體問題具體分析。
十、總結(jié)
線程池是項目中常用的,需要理解線程池的應用場景和構(gòu)造函數(shù),正確的使用線程池。
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java組件smartupload實現(xiàn)上傳文件功能
這篇文章主要為大家詳細介紹了java組件smartupload實現(xiàn)上傳文件功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10SpringBoot 策略模式實現(xiàn)切換上傳文件模式
策略模式是指有一定行動內(nèi)容的相對穩(wěn)定的策略名稱,這篇文章主要介紹了SpringBoot 策略模式 切換上傳文件模式,需要的朋友可以參考下2023-11-11微服務Spring Boot 整合 Redis 實現(xiàn)UV 數(shù)據(jù)統(tǒng)計的詳
這篇文章主要介紹了微服務Spring Boot 整合 Redis 實現(xiàn) UV 數(shù)據(jù)統(tǒng)計,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-01-01