文章目錄
-
- 1、yield
- 2、線程優先級
- 3、join
- 4、sleep
- 5、interrupt
- 6、兩階段終止模式
- 7、park
- 8、守護線程
線程常用方法
1、yield
- 調用yield使目前線程沖Running狀态轉為Runnable狀态,讓具有相同優先級的其他線程獲得運作機會。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程排程程式再次選中。
- 具體實作依賴于作業系統的排程器
2、線程優先級
- Java 線程優先級使用 1 ~ 10 的整數表示:
- 最低優先級 1:
Thread.MIN_PRIORITY
- 最高優先級 10:
Thread.MAX_PRIORITY
- 普通優先級 5(預設優先級):
Thread.NORM_PRIORITY
- 最低優先級 1:
- 擷取線程優先級 :線程對象.getPriority()
- 設定線程優先級: 先處對象.setPriority()
- 高優先級的線程比低優先級的線程有更高的幾率得到執行,實際上這和作業系統及虛拟機版本相關,有可能即使設定了線程的優先級也不會産生任何作用。
參考文章:Java 多線程:線程優先級
3、join
- 線程對象.join():無限時同步,目前線程一直等待調用join()方法的線程執行完畢,才繼續執行
- 線程對象.join(long millis):限時同步,最多等待millis毫秒後,目前線程繼續執行
private static int r = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
r = 10;
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
log.debug("r = " + r);
結果:r=0,如何讓程式列印10呢?想要列印10,主線程必須等待t1線程執行完畢,此時可在列印之前執行t1.join()方法
// 其他代碼與上面相同...
t1.start();
t1.join();
log.debug("r = " + r);
當給join設定參數時,結果又會如何呢?
t1.start();
t1.join(100);
log.debug("r = " + r);
結果:r=0
4、sleep
sleep
- 調用sleep方法,目前線程從Running狀态轉為Timed Waiting狀态
- 其他線程可以執行interrupt方法打斷在在睡眠的線程,這時會抛出InterruptedException
- 調用sleep方法,目前線程不會釋放對象鎖标記
- 建議使用TimeUnit的sleep代替Thread的sleep來獲得更好的可讀性
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
log.debug("t1 線程執行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
while (true) {
TimeUnit.SECONDS.sleep(1);
log.debug("主線程執行...");
}
}
用Thread的sleep方法設定睡眠1s和用TimeUnit的sleep設定睡眠1s,可以很直覺的看出後一種的可讀性更高。
TimeUnit還可以設定納秒、微妙、秒、分鐘、小時、天等等機關。
5、interrupt
- 可以打斷正常運作的線程,此時打斷标記為true,此時不會清除打斷标記(值為true)
- 可以打斷調用sleep()、wait()、join()等處于阻塞狀态的線程,此時會抛出InterruptedException異常;同時會清除打斷标記,既打斷标志設定為false
- 擷取打斷标記方法isInterrupted(),值為true/false。
@Slf4j(topic = "cm.test01")
public class CMInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("sleep...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(100);
log.debug("interrupt...");
t1.interrupt();
log.debug("t1 的打斷标記:{}", t1.isInterrupted());
}
}
2021-08-05 20:55:20 DEBUG [Thread-1] cm.test01 - sleep...
2021-08-05 20:55:20 DEBUG [main] cm.test01 - interrupt...
2021-08-05 20:55:20 DEBUG [main] cm.test01 - t1 的打斷标記:false
java.lang.InterruptedException: sleep interrupted
在來看下打斷正常運作的線程:
@Slf4j(topic = "cm.CMInterruptNormal")
public class CMInterruptNormal {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
}
}, "t1");
t1.start();
Thread.sleep(100);
log.debug("interrupt...");
t1.interrupt();
log.debug("t1 的打斷标記:{}", t1.isInterrupted());
}
}
2021-08-05 21:01:41 DEBUG [main] cm.CMInterruptNormal - interrupt...
2021-08-05 21:01:41 DEBUG [main] cm.CMInterruptNormal - t1 的打斷标記:true
線程t1一直在運作,那麼當線程被打斷了,如果讓線程t1停下來呢?此時,可以通過判斷标記來結束線程執行。
// 其他代碼同上
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) break;
}
}, "t1");
6、兩階段終止模式
一般應用于需要随系統一直執行的某些子產品,比如監控子產品等。此時,監控子產品設定為while(true),監控子產品在不需要實施執行時,用sleep()模拟;那麼監控子產品可以處于2種狀态:1、執行正常的監控記錄等程式 2、處于睡眠狀态。那麼當線程被打斷時,如何使在可控的情況下結束監控子產品,而不影響系統其他子產品的運作呢?
圖示:[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Lr95RLtg-1628172174411)(I:\study\java\note\concurrent\phrase2-termination-mode.png)]
代碼:
@Slf4j(topic = "cm.CMPhase2terminationMode")
public class CMPhase2terminationMode {
public static void main(String[] args) throws InterruptedException {
Thread monitor = new Thread(() -> {
while (true) {
// 判斷是否打斷
if (Thread.currentThread().isInterrupted()) {
log.debug("處理善後");
break;
}
try {
// 睡眠
TimeUnit.SECONDS.sleep(1);
// 監控記錄
log.debug("監控記錄執行...");
} catch (InterruptedException e) {
// 設定打斷标記
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}, "t1");
log.debug("監控子產品開始執行...");
monitor.start();
TimeUnit.MILLISECONDS.sleep(3500);
// 模拟意外打斷情況
monitor.interrupt();
log.debug("監控子產品關閉...");
}
}
如果不在catch子產品中添加重新設定打斷标記,那麼程式就不會結束。
7、park
- LockSupport的方法,會讓線程無限時暫停執行
- interrupt()方法可打斷暫停狀态,讓線程繼續執行
- 打斷标記為true時,park方法不會生效
- interrupted()判斷打斷标記,同時會清除打斷标記
代碼測試:
@Slf4j(topic = "cm.CMPark")
public class CMPark {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
log.debug("線程狀态:{}", Thread.currentThread().isInterrupted());
LockSupport.park();
log.debug("unpark...");
});
t1.start();
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
}
}
2021-08-05 21:50:13.084 DEBUG [Thread-1] cm.CMPark - park...
2021-08-05 21:50:14.083 DEBUG [Thread-1] cm.CMPark - unpark...
2021-08-05 21:50:14.083 DEBUG [Thread-1] cm.CMPark - 線程狀态:true
2021-08-05 21:50:14.084 DEBUG [Thread-1] cm.CMPark - unpark...
1秒之後執行打斷,當再次執行park的時候,沒有生效,此時,可以通過interrupted()方法判斷并清除标記
log.debug("unpark...");
log.debug("線程狀态:{}", Thread.interrupted());
LockSupport.park();
8、守護線程
Java程序,隻有當其所有線程執行結束的時候,才會結束。守護線程,顧名思義,不管守護線程有沒有執行完成,目前守護的線程結束的時候,守護線程也會結束執行。
代碼示例:
@Slf4j(topic = "cm.CMDaemon")
public class CMDaemon {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) break;
}
log.debug("守護線程執行結束...");
}, "t1");
t1.setDaemon(true);
t1.start();
TimeUnit.SECONDS.sleep(1);
log.debug("主線程執行結束...");
}
}
tips:
- 如果一個線程要設定為守護線程,通過調用setDaemon(true)方法,一定要線上程啟動之前設定,否則無效