一份珍貴的多線程學習筆記
多線程理解學習:
多線程
百度百科:多線程(multithreading),是指從軟件或硬件上實現多個線程并發(fā)執(zhí)行的請求具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進而提升整體處理性能。具有這種能力的系統(tǒng)包括對稱多處理機、多核心處理器以及芯片級多處理或同時多線程處理器。在一個程序中,這些獨立運行的程序片段叫做“線程”(Thread),利用它編程的概念就叫做“多線程處理”。
進程與線程區(qū)別:
進程:一個程序就是一個進程例如我正在運行的微信,網易云等都屬于一個進程。
線程:線程是運行在計算機操作系統(tǒng)上運算調度最小的單位,它包含在我們的進程中,在統(tǒng)一進程中線程擁有該進程的全部系統(tǒng)資源。
并行與并發(fā):
舉個例子:
例1:
你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發(fā)也不支持并行。
你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續(xù)吃飯,這說明你支持并發(fā)。 (不一定是同時的)
你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持并行。
例2:
你在打游戲,女朋友突然打來了視頻電話,如果你無視女朋友的電話,一直到游戲結束才回她電話,這就說明你不支持并發(fā),也不支持并行。
你在打游戲中,女票來了電話,你趕緊接了電話,放下了手頭的游戲,等你女票電話結束你才繼續(xù)玩你的游戲,這說明你支持并發(fā)。(不一定是同時的)
你玩游戲玩到一半,女票來了電話,你一邊接電話一邊打你的游戲說明你支持并行。
并發(fā)的關鍵是你有處理多個任務的能力,不一定要同時;
并行的關鍵是你有同時處理多個任務的能力。
所以我認為它們最關鍵的點就是:是否是『同時』。并發(fā)是輪流處理多個任務,并行是同時處理多個任務
線程池:
線程的創(chuàng)建方式:Java就是一個天生的多線程語言。在我們運行main方法的時候,其實就是創(chuàng)建并啟動了一個main線程。
線程的狀態(tài)(五種):
1、新建:新創(chuàng)建一個線程。
2、就緒:線程對象創(chuàng)建后,其他線程(比如main線程)調用了該對象的start( )方法。該狀態(tài)的線程位于可運行線程池中,等待被線程調度選中,獲取CPU的使用權。
3、運行:獲取了CPU的使用權。
4、阻塞:線程可能被掛起,或者被中斷,讓出CPU的使用權。
5、死亡:線程run()、main()方法執(zhí)行結束,或者因異常退出了run()方法。則該線程結束生命周期。死亡的線程不可再次復生。

在Java中創(chuàng)建線程有三種方式:
1、通過集成Thread類,重寫run()方法創(chuàng)建線程

輸出結果:

2、通過實現Runnable接口創(chuàng)建線程

輸出結果:

3、通過實現Callable接口和Future創(chuàng)建線程,該方式需要通過FutureTask幫助獲取返回值。

輸出結果:

為什么需要線程池?
線程的創(chuàng)建和銷毀都會消耗系統(tǒng)的資源,將線程放在一個緩存池,需要使用時直接從緩存池中獲取線程,通過重用使用已創(chuàng)建的線程來降低系統(tǒng)所消耗的能源。
線程池的使用:
在Executor類中有4種線程池的創(chuàng)建方法。
1、newCachedThreadPool():創(chuàng)建一個緩存線程池,創(chuàng)建線程數量不限制,線程長時間未使用會被回收,如果有大量線程同時運行可能會導致系統(tǒng)癱瘓。SynchronousQueue(同步隊列):內部只能包含一個元素的隊列。

2、newFixedThreadPool():創(chuàng)建固定線程的線程池,LinkedBlockingQueue隊列中大小是不限的,所有可能會出現內存溢出的情況。

3、newSIngleTreadExecutor()創(chuàng)建一個只有一個線程的線程池,俗稱單例線程池同樣也是采用的LinkedBlockingQueue.

4、newScheduledThreadPool():創(chuàng)建一個定長的線程池,而且支持定時的以及周期性的任務執(zhí)行,支持定時及周期性任務執(zhí)行。

線程池使用注意事項:
阿里開發(fā)規(guī)范(原文):
【強制】線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方式,這
樣的處理方式讓寫的同學更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風險。
說明:Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2) CachedThreadPool:
允許的創(chuàng)建線程數量為 Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導致 OOM。
ThreadPoolExecutor:

參數說明:
corePoolSize:核心線程數
maximumPoolSize:最大線程數
keepAliveTime:線程保持活躍時間
unit:線程活躍時間的單位
workQueue:任務隊列
threadFactory;線程工廠,線程創(chuàng)建的方式
handler:拒絕策略
TimeUnit類,配合keepAliveTime使用指定時間格式
納秒 NANOSECONDS
微秒 MICROSECONDS
毫秒 MILLISECONDS
秒 SECONDS
分 MINUTES
時 HOURS
天 DAYS
拒絕策略
RejectedExecutionHandler:ThreadPoolExecutor提供了四種拒絕策略
1、AbortPolicy(默認使用):拋出異常

2、CallerRunsPolicy:在當前線程運行該任務

3、DiscardPolicy:丟棄任務

4、DiscardOldestPolicy:丟棄最早的任務

線程池的實現原理:
當有新任務時,會創(chuàng)建線程來執(zhí)行任務,當線程數達到corePoolSize時,就會將任務放在阻塞隊列,當阻塞隊列滿了,并且線程數達到了maximumPoolSize時,會觸發(fā)拒絕策略拒絕任務。

鎖:
什么時候需要用到鎖:
多線程的環(huán)境下肯定會出現線程安全的問題,通過鎖可以解決線程的安全問題,保證數據的一致性。
鎖升級:
鎖狀態(tài):偏向鎖,輕量級鎖,重量級鎖(級別由低到高)
1. 偏向鎖:
大多數情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單地測試一下對象頭的Mark Word里是否存儲著指向當前線程的偏向鎖。如果測試成功,表示線程已經獲得了鎖。如果測試失敗,則需要再測試一下Mark Word中偏向鎖的標識是否設置成1(表示當前是偏向鎖):如果沒有設置,則使用CAS競爭鎖;如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程。
2. 輕量級鎖:
線程在執(zhí)行同步塊之前,JVM會先在當前線程的棧楨中創(chuàng)建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復制到鎖記錄中,官方稱為Displaced MarkWord。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
3. 輕量級解鎖時,會使用原子的CAS操作將Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競爭發(fā)生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖。一旦鎖升級成重量級鎖,就不會再恢復到輕量級鎖狀態(tài)。
volatile:
Java中的關鍵字,基于jvm實現??梢员WC被他修飾的方法或是代碼塊在任意一個時刻只能有一個線程執(zhí)行。
synchronized實現原理

synchronized使用方式
Java中所有對象都可以被當做synchronized的鎖。
1、synchronized使用在普通方法中,鎖是當前對象,進入被synchronized修飾的普通方法時要獲取當前對象的鎖。

輸出結果:

2、synchronized使用在靜態(tài)方法中,鎖是當前class對象,進入被synchronized修飾的靜態(tài)方法時要獲取當前的class對象鎖。

輸出結果:

3、synchronized使用在代碼塊中,鎖是代碼塊指定對象的鎖,進入被synchronized修飾的代碼塊時需要獲取到括號中對象的鎖。

輸出結果:

Lock鎖
在Lock接口出現之前,只能靠synchronized關鍵字實現鎖功能的,在JDK1.5后,并發(fā)包中新增了Lock接口(以及相關實現類)用來實現鎖功能,它提供了與synchronized關鍵字類似的同步功能,并且讓我們可以比synchronized為靈活的運行性鎖。自己管理鎖則需要手動獲取鎖和釋放鎖,使用不當就會造成系統(tǒng)癱瘓,比如死鎖。

CAS(Compare and Swap)
Compare and Swap:比較并替換,CAS是區(qū)別于synchronize的一種樂觀鎖。CAS是一種無鎖算法,CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。在sun.misc包下有個被final關鍵字修飾的Unsafe的類,該類不對外提供。該類中的方法都被native關鍵字修飾,表示該方法由本地實現,Unsafe底層均是采用的C++語言實現的,保證在cpu上運行的原子性。Unsafe類只提供了三種原子操作方法,其他類型都是轉成這三種類型再使用對應的方法去原子更新的。
AQS(AbstractQueuedSynchronizer)
AQS全稱:AbstractQueuedSynchronizer是一個同步隊列(它是一個抽象類),AQS底層使用了模板方法模式實現了對同步狀態(tài)的管理,對阻塞線程進行排隊,等待通知等等。AQS的核心也包括了這些方面:同步隊列,獨占鎖的獲取和釋放,共享鎖的獲取和釋放以及可中斷鎖,超時等待鎖獲取這些特性的實現。AbstractQueuedSynchronizer是一個FIFO(First Input First Output)即先進先出的隊列。

獨占鎖

獨占釋放鎖

公平鎖

ReentrantLock相關操作

公平鎖與非公平鎖字面上意思就是一個是公平的競爭,一個是非公平的競爭。
ReentrantLock默認使用的是非公平鎖,因為非公平鎖可以減少線程間的切換,可以避免資源的浪費,也可能會導致線程一直處于阻塞狀態(tài)。Doug Lea大神選擇了非公平鎖這個也很符合人類的生活,這個世界是不公平的,因為有了種種的不公平,我們的生活才可以如此的絢麗多彩,想象一下,如果這個世界是公平的,人人平等,那該會有多枯燥。
本文到這里就結束了~感謝小伙伴的閱讀哦~
什么?沒看夠?想要獲取更多知識點?評論回復“666”,尚學堂最新的教學資料就是你的啦~

