第十一天、多線程
線程安全問題
線程安全問題
-
多線程給我們帶來了很大性能上的提升,但是也可能引發線程安全問題
-
線程安全問題指的是當個多線程同時操作同一個共享資源的時候,可能會出現的操作結果不符預期問題
線程同步方案
認識線程同步
線程同步
-
線程同步就是讓多個線程先后依次訪問共享資源,這樣就解決了安全問題,它最常見的方案就是加鎖
-
加鎖 : 每次只允許一個多線程加鎖,加鎖后才能進入訪問 ,訪問完畢后自動解鎖,之后其它線程才能加鎖進來
方式一 : 同步代碼塊
同步代碼塊
-
作用 : 把訪問共享資源的核心代碼給上鎖,以此保證線程安全
-
synchronized(同步鎖) {訪問共享資源的核心代碼 }
-
原理 : 每次只允許一個線程加鎖后進入,執行完畢后自動解鎖,其他線程才可以進來執行
同步鎖的注意事項
-
對于當前同時執行的線程來說,同步鎖必須是同一把鎖(同一個對象),否則會出bug
-
對于實列方法建議使用this作為鎖對象
-
對于靜態方法建議使用字節碼(類名.class)對象作為鎖對象
方式二:同步方法
同步方法
-
作用:把訪問共享資源的核心方法給上鎖,以此保證線程安全
-
修飾符 synchronized 返回值類型 方法名稱(形參列表) {操作共享資源的代碼 }
-
原理:每次只能一個線程進入,執行完畢以后自動解鎖,其他線程才可以進來執行
同步方法底層原理
-
同步方法其實底層也是有隱式鎖對象的,只是鎖的范圍是整個方法代碼
-
如果方法是實例方法:同步方法默認用this作為的鎖對象
-
如果方法是靜態方法:同步方法默認用類名.class作為的鎖對象
方式三:Lock鎖
Lock鎖
-
Lock鎖是JDK5開始提供的一個新的鎖定操作,通過它可以創建出鎖對象進行加鎖和解鎖,更靈活、更方便、更強大
-
Lock是接口,不能直接實例化,可以采用它的實現類ReentrantLock來構建Lock鎖對象
Lock的常用方法
線程池
認識線程池
當前創建線程的問題
-
用戶每發起一個請求,后臺就需要創建一個新線程來處理,任務處理完畢之后,線程就會被銷毀
-
下次新任務來了肯定又要創建新線程處理的,用完又要被銷毀
-
而創建和銷毀線程的開銷是很大的,當請求過多時,肯定會產生大量的線程出來,這樣會嚴重影響系統的性能
什么是線程池
-
線程池就是一個可以復用線程的技術
-
它就像一個大的池子一樣,里面可以放置一些線程,當需要的時候,就從里面取出來用,用完了就還回去
-
如此一來,就不必頻繁的創建和銷毀線程了,大大的提高了線程的利用率,提供系統的性能
線程池的工作原理以及執行流程
-
判斷核心線程數是否已滿,如果沒滿,則創建一個新的核心線程來執行任務
-
如果核心線程滿了,則判斷工作隊列是否已滿,如果沒滿,則將任務存儲在這個工作隊列
-
如果工作隊列滿了,則判斷最大線程數是否已滿,如果沒滿,則創建臨時線程執行任務
-
如果最大線程數已滿,則執行拒絕策略
如何創建線程池?
誰代表線程池?
-
JDK 5.0起提供了代表線程池的接口:ExecutorService
如何得到線程池對象?
-
使用ExecutorService的實現類ThreadPoolExecutor自創建一個線程池對象
ThreadPoolExecutor
-
參數一:corePoolSize : 指定線程池的核心線程的數量
-
參數二:maximumPoolSize:指定線程池的最大線程數量
-
參數三:keepAliveTime :指定臨時線程的存活時間
-
參數四:unit:指定臨時線程存活的時間單位(秒、分、時、天)
-
參數五:workQueue:指定線程池的任務隊列
-
參數六:threadFactory:指定線程池的線程工廠
-
參數七:handler:指定線程池的任務拒絕策略(線程都在忙,任務隊列也滿了的時候,新任務來了該怎么處理)
任務緩沖隊列
任務拒絕策略
線程池處理Runnable任務
ExecutorService的常用方法
線程池處理Callable任務
ExecutorService的常用方法
Executors工具類實現線程池
Executors
-
是一個線程池的工具類,提供了很多靜態方法用于返回不同特點的線程池對象
-
注意 :這些方法的底層,都是通過線程池的實現類ThreadPoolExecutor創建的線程池對象
Executors使用可能存在的陷阱
-
大型并發系統環境中使用Executors如果不注意可能會出現系統風險
線程通信(了解)
什么是線程通信?
-
當多個線程共同操作共享的資源時,線程間通過某種方式互相告知自己的狀態,以相互協調,并避免無效的資源爭奪
線程通信的常見模型(生產者與消費者模型)
-
生產者線程負責生產數據
-
消費者線程負責消費生產者生產的數據
-
注意:生產者生產完數據應該等待自己,通知消費者消費;消費者消費完數據也應該等待自己,再通知生產者生產
Object類的等待和喚醒方法
注意
-
上述方法應該使用當前同步鎖對象進行調用
理論補充
進程與線程
進程與線程
-
進程:正在運行的程序(軟件)就是一個獨立的進程
-
線程:線程是屬于進程的,一個進程中可以同時運行很多個線程
-
關系:進程=火車 線程=車廂
并發與并行
并發的含義
-
進程中的線程是由CPU負責調度執行的,但CPU能同時處理線程的數量有限,為了保證全部線程都能往前執行, CPU會輪詢為系統的每個線程服務,由于CPU切換的速度很快,給我們的感覺這些線程在同時執行,這就是并發
并行的含義
-
在同一個時刻上,同時有多個線程在被CPU調度執行
多線程到底是怎么在執行的?
-
并發和并行同時進行的
線程生命周期
線程的生命周期和狀態
-
也就是線程從生到死的過程中,經歷的各種狀態及狀態轉換,Java總共定義了6種狀態
線程的6種狀態互相轉換