前言:要秋招了,複習一下應對秋招,糾結該先看啥,最後決定先學習《Java高併發編程詳解》,此博客爲看書所寫的筆記,因爲是筆記,所以會只記比較重要的東西,不適合初學者。
參考:
https://blog.csdn.net/q610376681/article/details/87922812
目錄
第一章 快速認識線程
當JVM啓動時啓動了一個進程,如果在進程中增加線程呢?用Thread.start即可,Thread類的對象在沒調用start方法時僅僅是一個對象,調用了才能啓動一個線程。
JVM啓動時創建的不知有main線程,還有一些垃圾回收線程,RMI線程等等。
1.1 線程的生命週期
NEW是Thread對象還沒調用start方法時的狀態
Runnable是調用Start方法但是還沒獲取到CPU的調度資格,處於此狀態下的線程只能意外終止或者進入Running狀態
Running狀態是獲取到CPU正在運行的線程,可以通過stop直接進入Terminated狀態,也可能因爲調用sleep,wait,調用了某個阻塞的IO,需要獲取某個鎖資源而進入Blocked狀態,或者是因爲cpu輪轉或者yield放棄cpu而進入Runnable狀態。
Blocked狀態是線程進入了阻塞狀態,可以直接進入Terminated狀態,可以因休眠結束、被喚醒、獲取到了鎖資源、阻塞過程中被打斷而進入Runnable狀態。
Terminated是線程的最終形態,該狀態下不會切換到任何狀態
注:一個Thread只能start一次
1.2 模板設計模式在Thread中的應用
我們都知道最終start會調用run方法來新開線程,這其實是典型的模板模式,父類編寫算法結構代碼,子類實現邏輯細節。
在這裏父類開啓線程,子類重寫run方法實現具體細節。
模板模式介紹:https://www.jianshu.com/p/800a44c1d9dd
1.3 策略模式在Thread中的使用
當我們模擬一個多線程叫號程序時,當前的號碼是共享資源,可以通過將共享變量置爲static來解決,但是如果共享資源很多且要經過比較複雜的計算呢?不可能都用static修飾,Java提供了一個接口Runnable專門解決該問題,將線程的控制和業務邏輯的運行分離開來。
TicketWindow extend Threads {
private static int index = 1;
}
我們新建類實現接口,將具體的業務邏輯在接口中完成,然後新建thread時將接口實現類傳入,它便會調用我們接口實現的方法,完成相應的業務邏輯。
策略模式講解:https://baijiahao.baidu.com/s?id=1638224488060180625&wfr=spider&for=pc
第二章 深入理解Thread構造函數
2.1 線程的命名
線程默認以Thread-作爲前綴與一個自增數字進行組合,也可在新建的時候進行命名,線程啓動之前可以修改名字,啓動後名字不能修改。
2.2 線程的父子關係
任何創建的線程都會有一個父線程,一個線程的創建肯定是由另一個線程完成的,被創建線程的父線程是創建它的線程。
2.3 Thread與ThreadGroup
Thread的構造函數中,可以顯式地指定線程的group,如果不指定,則子線程會加入父線程所在的線程組,main線程所在的ThreadGroup稱爲main
2.4 Thread與JVM虛擬機棧
Thread的構造函數中,有一個特殊的參數stackSize。
一般情況下,創建線程的時候不會手動指定棧內存的地址空間字節數組,統一通過xss參數進行設置即可,stacksize越大表示線程內方法調用遞歸的深度就越深,stacksize越小則代表創建的線程數據量越多。
2.4.1 JVM內存結構
程序計數器:無論任何語言都需要由操作系統通過控制總線向CPU發送機器指令,程序計數器在JVM中所起的作用就是用於存放當前線程接下來將要執行的字節碼指令、分支、循環、跳轉、異常處理等信息,任何時候,一個處理器只執行其中一個線程中的指令,爲了能夠在CPU時間片輪轉切換上下文之後順利回到正確的執行位置,每條線程都需要具有獨立的程序計數器。
Java虛擬機棧:這也是線程私有的,用於存放局部變量表、操作棧、動態鏈接、方法出口等信息,每個棧幀存放對應時刻的以上信息,方法的調用對應着棧幀的出棧和入棧。
本地方法棧:Java中提供了調用本地方法的接口,也就是C/C++程序,一些網絡通信,文件操作的底層便需要調用本地方法,JVM爲本地方法所劃分的內存區域便是本地方法棧。
堆內存:堆內存是JVM中最大的一塊內存區域,被所有的線程所共享,Java在運行期間創建的所有對象幾乎都存放在該內存區域,因此該區域也是垃圾回收器重點照顧的區域,因此有些時候堆內存被稱爲“GC堆”,堆內存一般被分爲新生代和老年代,更細緻的分爲Eden區、From Survivor區和To Survivor區。
方法區:方法區也是多個線程所共享的內存區域,它主要用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器(JIT)編譯後的代碼等數據,雖然在Java虛擬機規範中,將方法區分爲堆內存的一個邏輯分區,但是它還是經常被成爲非堆,有時也被稱爲持久代。
Java 8元空間:上面介紹了JVM的內存劃分,在JDK1.8之前內存大概都是這樣劃分的,但是自從JDK1.8起,JVM的內存區域發生了一些改變,實際上是持久代被徹底刪除,取而代之的是元空間。
2.4.2 Thread與虛擬機棧的關係
可以粗略的認爲 Java進程內存=堆內存+棧內存*線程個數
當虛擬機棧越大,其它不變,可創建的線程數量就越小;當堆內存越大,可創建的線程數量也會越小,但影響不明顯
一個粗略估算最大線程數量的方法
線程數量=((最大地址空間)-JVM堆內存-系統保留內存)/ThreadStackSize
2.5 守護線程
當JVM中沒有個一個非守護線程,既JVM中全是守護線程時JVM會退出。
如垃圾回收線程是守護線程,當非守護線程main結束後垃圾回收線程會自動結束。
線程可以通過setDaemon設置守護或非守護線程。