join()
join就是指等待該線程結(jié)束,然后才繼續(xù)往下執(zhí)行自身線程煌恢,如果該線程已經(jīng)結(jié)束骇陈,
對實例調(diào)用join()會立刻返回。此外瑰抵,join(long)的重載方法也可以指定一個等待時間你雌,超過等待時間后就不再繼續(xù)等待。
如果線程處于等待狀態(tài)谍憔,例如匪蝙,t.join()會讓main線程進入等待狀態(tài),此時习贫,如果對main線程調(diào)用interrupt()逛球,join()方法會立刻拋出InterruptedException,因此苫昌,目標線程只要捕獲到join()方法拋出的InterruptedException颤绕,就說明有其他線程對其調(diào)用了interrupt()方法,通常情況下該線程應該立刻結(jié)束運行祟身。
中斷線程的兩種方法
1.interrupt()方法
目標線程需要反復檢測自身狀態(tài)是否是interrupted狀態(tài)奥务,如果是,就立刻結(jié)束運行袜硫。
當有join方法的時候氯葬,就捕捉是否有InterruptedException,有的話就結(jié)束進程
2.設置標志位
public class Main {
public static void main(String[] args) throws InterruptedException {
HelloThread t = new HelloThread();
t.start();
Thread.sleep(1);
t.running = false; // 標志位置為false
}
}
class HelloThread extends Thread {
public volatile boolean running = true;
public void run() {
int n = 0;
while (running) {
n ++;
System.out.println(n + " hello!");
}
System.out.println("end!");
}
}
在這里共享變量用volitale修飾保證內(nèi)存可見性
守護進程
Thread t = new MyThread();
t.setDaemon(true);
t.start();
守護線程是指為其他線程服務的線程婉陷。在JVM中帚称,所有非守護線程都執(zhí)行完畢后放案,無論有沒有守護線程迷雪,虛擬機都會自動退出。
因此梧奢,JVM退出時担神,不必關心守護線程是否已結(jié)束楼吃。
同時也需要注意,守護線程不能持有需要關閉的資源(如打開文件等)
線程安全
sychronized
(1)修飾普通方法
(2)修飾靜態(tài)方法
對靜態(tài)方法的同步本質(zhì)上是對類的同步
(3)修飾代碼塊
sychronized的原理
其實它的本質(zhì)就是獲取監(jiān)視器鎖(monitor)
每個對象有一個監(jiān)視器鎖(monitor)妄讯。當monitor被占用時就會處于鎖定狀態(tài)孩锡,線程執(zhí)行monitorenter指令時嘗試獲取monitor的所有權,過程如下:
1捞挥、如果monitor的進入數(shù)為0浮创,則該線程進入monitor,然后將進入數(shù)設置為1砌函,該線程即為monitor的所有者斩披。
2溜族、如果線程已經(jīng)占有該monitor,只是重新進入垦沉,則進入monitor的進入數(shù)加1.
3.如果其他線程已經(jīng)占用了monitor煌抒,則該線程進入阻塞狀態(tài),直到monitor的進入數(shù)為0厕倍,再重新嘗試獲取monitor的所有權寡壮。
這個過程也能反應出sychronized是個可重入鎖。
另外讹弯,除了sychronized同步的代碼是線程安全的以外况既,Java標準庫的java.lang.StringBuffer也是線程安全的。
還有一些不變類组民,例如String棒仍,Integer,LocalDate臭胜,它們的所有成員變量都是final莫其,多線程同時訪問時只能讀不能寫,這些不變類也是線程安全的耸三。后乱陡,類似Math這些只提供靜態(tài)方法,沒有成員變量的類仪壮,也是線程安全的憨颠。
除了上述幾種少數(shù)情況,大部分類积锅,例如ArrayList烙心,都是非線程安全的類,我們不能在多線程中修改它們乏沸。但是,如果所有線程都只讀取爪瓜,不寫入蹬跃,那么ArrayList是可以安全地在線程間共享的。
死鎖
因為sychronized是可重入鎖铆铆,
(JVM允許同一個線程重復獲取同一個鎖蝶缀,這種能被同一個線程反復獲取的鎖,就叫做可重入鎖薄货。)
所以可能會出現(xiàn)翁都,兩個線程各自持有不同的鎖,然后各自試圖獲取對方手里的鎖谅猾,造成了雙方無限等待下去柄慰,這就是死鎖鳍悠。
解決辦法是,獲取鎖的順序必須要一致坐搔。
線程池
為什么使用線程池:
1.減少了創(chuàng)建和銷毀線程的次數(shù)藏研,每個工作線程都可以被重復利用,可執(zhí)行多個任務概行。
2.可以根據(jù)系統(tǒng)的承受能力蠢挡,調(diào)整線程池中工作線線程的數(shù)目,防止因為消耗過多的內(nèi)存
callable接口凳忙,返回值是future
常用的幾種線程池
1.Executors.newCacheThreadPool():可緩存線程池
2.Executors.newFixedThreadPool(int n):創(chuàng)建一個可重用固定個數(shù)的線程池
3.Executors.newScheduledThreadPool(int n):創(chuàng)建一個定長線程池业踏,支持定時及周期性任務執(zhí)行
4 Executors.newSingleThreadExecutor():創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務涧卵,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行勤家。
corePoolSize,線程池大小
maximumPoolSize艺演,任務量忽然增多時却紧,可以達到的最大線程數(shù)
largestPoolSize,記錄曾經(jīng)有過的最大線程數(shù)目胎撤。