1. 線程和進程
- 進程是代碼在數(shù)據(jù)集合上的一次運行活動, 是系統(tǒng)進行資源分配和調度的基本單位。
- 線程則是進程的一個執(zhí)行路徑, 一個進程中至少有一個線程肥卡,進程中的線程共享進程的資源。線程是CPU 分配的基本單位事镣。
- Java中步鉴,多個線程共享進程的堆和方法區(qū)資源,每個線程有自己的程序計數(shù)器和棧區(qū)域璃哟。
-
程序計數(shù)器:
(1) 記錄了該線程讓出CPU時的執(zhí)行地址氛琢,待再次分配到時間片時線程就可以從計數(shù)器指定的地址繼續(xù)執(zhí)行。
(2) 只有執(zhí)行的是Java 代碼時記錄的才是下一條指令的地址随闪,而對于native方法記錄的是undefined 地址阳似。 -
棧資源:
(1) 存儲該線程的局部變量
(2) 存放線程的調用棧幀 -
堆:
(1) 堆里面主要存放使用new 操作創(chuàng)建的對象實例
(2) 堆是被進程中的所有線程共享的 - 方法區(qū)用來存放JVM加載的類、常量及靜態(tài)變量等信息铐伴,是線程共享的撮奏。
2. 線程創(chuàng)建
- 繼承Thread 類并重寫run方法
(1) 創(chuàng)建完thread對象后該線程并沒有被啟動執(zhí)行,調用了start 方法后才真正啟動了線程当宴。
(2) start 方法后線程處于就緒狀態(tài)畜吊,等待獲取CPU 資源。
(3) 在run()方法內(nèi)可以使用this獲取當前線程户矢。
//創(chuàng)建線程
MyThread thread= new MyThread();
// 啟動線程
thread .start();
- 實現(xiàn)Runnable接口的run 方法
(1) 多個線程可共用一個task 代碼邏輯玲献。
(2) 在實現(xiàn)接口的同時可以繼承其他類。
RunableTask task =new RunableTask();
new Thread(task).start() ;
new Thread(task).start() ;
- 使用FutureTask 方式
(1) 實現(xiàn)Callable 接口的call()方法。
(2) 可以拿到任務的返回結果捌年。
static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception{}
}
//創(chuàng)建異步任務
FutureTask<String> futureTask =new FutureTask<>(new CallerTask()) ;
//啟動線程
new Thread(futureTask).start () ;
try {
//等待任務執(zhí)行完畢瓢娜,并返回結果
String result = futureTask.get ();
} catch (ExecutionException e) {}
3. 線程通知與等待
-
wait()函數(shù)
當一個線程調用一個共享變量的wait()方法時,該調用線程會被阻塞掛起延窜,直到發(fā)生以下情況才返回:
(1) 其他線程調用了該共享對象的notify()或者notifyAll()方法恋腕;
(2) 其他線程調用了該線程的interrupt()方法抹锄,該線程拋出InterruptedException異常返回逆瑞。調用wait()方法的線程需要先獲取該對象的監(jiān)視器鎖,否則會拋出IllegalMonitorStateException異常
通過以下方式獲取監(jiān)視器鎖:
(1) 執(zhí)行synchronized 同步代碼塊時伙单, 使用該共享變量作為參數(shù)获高。
(2) 調用該共享變量的方法,并且該方法使用了synchronized 修飾吻育。
4. 線程睡眠
- sleep方法:
(1) 當一個執(zhí)行中的線程調用了Thread 的sleep方法后念秧,會暫時讓出指定時間的執(zhí)行權,不參與CPU的調度
(2) 該線程擁有的監(jiān)視器資源布疼,比如鎖還是持有不會讓出摊趾。
(3) 睡眠時間到了后該線程處于就緒狀態(tài),等待獲取cpu資源游两。
(4) 在睡眠期間其他線程調用了該線程的interrupt方法中斷了該線程砾层,則該線程會在調用sleep方法的地方拋出IntermptedException異常而返回。 - yield方法:
(1) 線程調用yield 方法時贱案,是在暗示線程調度器當前線程請求讓出自己的CPU 使用肛炮,讓線程調度器現(xiàn)在就可以進行下一輪的線程調度,但是線程調度器可以無條件忽略這個暗示宝踪。
(2) 調用yield 方法時后侨糟,當前線程會讓出CPU 使用權,然后處于就緒狀態(tài)瘩燥,線程調度器下一次調度時就有可能調度到當前線程執(zhí)行秕重。
5. 線程中斷
Java 中的線程中斷是一種線程間的協(xié)作模式,通過設置線程的中斷標志并不能直接終止該線程的執(zhí)行厉膀, 而是被中斷的線程根據(jù)中斷狀態(tài)自行處理溶耘。
interrupt方法:中斷線程
(1) 線程B可以調用線程A的interrupt方法來設置線程A 的中斷標志為true并立即返回。
(2) 如果線程A 因為調用了wait 系列函數(shù)站蝠、join 方法或者sleep 方法而被阻塞掛起汰具,會在調用這些方法的地方拋出InterruptedException 異常而返回。線程上下文切換時機有:
(1) 當前線程的CPU 時間片使用完處于就緒狀態(tài)時
(2) 當前線程被其他線程中斷時
6. 線程死鎖
死鎖是指兩個或兩個以上的線程在執(zhí)行過程中菱魔,因爭奪對方已經(jīng)持有的資源而造成的互相等待的現(xiàn)象留荔。
死鎖的產(chǎn)生必須具備四個條件:
互斥條件:該資源同時只由一個線程占用。
請求并持有條件: 指一個線程己經(jīng)持有了至少一個資源, 但又提出了新的資源請求聚蝶,而新資源己被其他線程占有杰妓,所以當前線程會被阻塞,但阻塞的同時并不釋放自己已經(jīng)獲取的資源碘勉。
不可剝奪條件: 指線程獲取到的資源在自己使用完之前不能被其他線程搶占巷挥。
環(huán)路等待條件:發(fā)生死鎖時, 必然存在一個線程-資源的環(huán)形鏈验靡。避免死鎖:
(1) 只有請求并持有和環(huán)路等待條件是可以被破壞的倍宾。
(2) 造成死鎖的原因其實和申請資源的順序有很大關系, 使用資源申請的有序性原則就可以避免死鎖胜嗓。
(3) 資源申請的有序性:假如線程A 和線程B 都需要資源1, 2, 3, ... , n 時高职,對資源進行排序,線程A 和線程B 只有在獲取了資源n-1 時才能去獲取資源n 辞州。
6. 守護線程與用戶線程
- Java 中的線程分為兩類怔锌,daemon 線程(守護線程〉和user 線程(用戶線程)。
- 守護線程
(1) 在JVM啟動的同時变过,啟動了好多守護線程埃元, 比如垃圾回收線程
(2) 守護線程是否結束并不影響JVM的退出,只要用戶線程都退出了媚狰,就會終止JVM進程岛杀。如果你希望在主線程結束后JVM進程馬上結束,那么在創(chuàng)建線程時可以將其設置為守護線程哈雏。
(3) 設置守護線程: thread.setDaemon(true) ;
(4) main線程運行結束后楞件, JVM會自動啟動一個叫作DestroyJava VM 的線程, 該線程會等待所有用戶線程結束后終止JVM進程裳瘪。
(5) 在默認情況下土浸, tomcat的接受線程和處理線程都是守護線程, 這意味著當tomcat收到shutdown 命令后并且沒有其他用戶線程存在的情況下tomcat 進程會馬上消亡彭羹,而不會等待處理線程處理完當前的請求黄伊。
7. ThreadLocal
- 提供了線程本地變量,如果創(chuàng)建了一個ThreadLocal 變量派殷,那么訪問這個變量的每個線程都會有這個變量的一個本地副本还最。
- 使用
//創(chuàng)建ThreadLocal 變量
ThreadLocal<String> localVariable = new ThreadLocal<> () ;
//在線程1中
localVariable.set("threadOne local variable");
//在線程2中
localVariable.set("threadTwo local variable");
//在某個線程中獲取變量
localVariable.get()
原理
(1) 在每個線程內(nèi)部都有一個名為threadLocals 的成員變量, 該變量的類型為HashMap毡惜,用于存儲線程本地變量拓轻。key 為我們定義的ThreadLocal變量的this引用, value 則為我們使用set 方法設置的值经伙。
(2) ThreadLocal只是相當于一個工具類扶叉,它通過set 方法把value 值放入調用線程的threadLocals 里面并存放起來, 當調用線程調用它的get 方法時,再從當前線程的threadLocals 變量里面將其拿出來使用枣氧。
(3) 為了防止內(nèi)存溢出溢十,在使用完后,記得通過ThreadLocal的remove方法移除變量达吞。
(4) 同一個ThreadLocal 變量在父線程中被設置值后张弛, 在子線程中是獲取不到的。InheritableThreadLocal: 讓子線程能訪問到父線程中的值
(1) InheritabIeThreadLocal 繼承了ThreadLocal酪劫,set和get操作的是該線程的成員變量inheritableThreadLocals吞鸭。
(2) 在創(chuàng)建線程時,會判斷當前線程(父線程)中的inheritableThreadLocals變量是否為空契耿,如果不為空瞒大,會把父線程的inheritableThreadLocals 成員變量的值復制到子線程的inheritableThreadLocals變量中。這樣子線程就擁有了一份父線程的可繼承的變量的副本搪桂。
(3) 父線程和子線程的相互影響關系可以查看https://blog.csdn.net/v123411739/article/details/79117430
對于可變對象:父線程初始化, 因為Thread Construct淺拷貝, 共用索引, 子線程修改父線程跟著變; 父線程不初始化, 子線程初始化, 無Thread Construct淺拷貝, 子線程和父線程都是單獨引用, 不同對象, 子線程修改父線程不跟著變。
對于不可變對象:不可變對象由于每次都是新對象, 所以無論父線程初始化與否盯滚,子線程和父線程都互不影響踢械。