啟動和終止執行緒
- 1.1 構造執行緒
- 1.2啟動執行緒
- 1.3理解中斷
- 1.4 過期的suspend()、resume()和stop()
- 1.5 安全的終止執行緒
- 結尾
通過上篇章節我們知道通過執行緒的stat()方法進行啟動,對著run()方法的執行完畢,執行緒也隨之終止,下面將詳細介紹執行緒的啟動和終止,
1.1 構造執行緒
在現場運行之前首先要構造一個執行緒物件,在構造的時候需要提供執行緒所需要的的屬性,如執行緒所屬的執行緒組、執行緒優先級、是否是Daemon執行緒等資訊,我們來看下Thread中物件池行程初始化的原始碼,
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name.toCharArray();
// 當前執行緒就是該執行緒的父執行緒
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
// 將daemon、priority屬性設定為父執行緒的對應屬性
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
// 將父執行緒的InheritableThreadLocal復制過來
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
// 分配一個執行緒ID
tid = nextThreadID();
}
一個新構造的執行緒物件時尤其parent執行緒來進行空間分配得,而child執行緒繼承了parent是否為Deamon、優先級和加載資源的contextClassLoader以及可繼承的ThreadLocal,同時還會分配一個唯一的ID來標識這個child執行緒,至此,一個可以運行的執行緒物件就初始化好了,在堆記憶體中等待運行,
1.2啟動執行緒
執行緒物件在初始化完畢之后,呼叫start()方法就可以啟動,執行緒start()方法的含義是:當前執行緒(即parent執行緒)同步告知Java虛擬機,只要執行緒規劃器空閑,立即啟動呼叫start()方法的執行緒,
1.3理解中斷
中斷可以理解為執行緒的一個表示為屬性,他表示一個運行中的執行緒是否被其他執行緒進行了中斷操作,其他執行緒通過呼叫該執行緒interrupt()方法對其進行中斷操作,
執行緒通過方法isInterrupt()來進行性判斷是否被中斷,也可以呼叫靜態方法Thread.interrupted()對當前執行緒的中斷標識位行程復位,如果該執行緒為終止狀態,即使該執行緒被中斷過,在呼叫該執行緒物件的*isInterrupt()*方法時還是會回傳false,
Java中有很多方法會拋出InterruptException(例如Thread.sleep(long millis)方法)例外,在這些方法拋出InterruptException一次之前,Java虛擬機會先將該執行緒的標識位清除,然后拋出InterruptException,此時再呼叫isInterrupt()方法回傳值為false,
我們通過代碼來觀察下,
public class Interrupt {
static class SleepRunner implements Runnable {
@Override
public void run() {
while (true) {
SleepUtils.second(10);
}
}
}
static class BusyRunner implements Runnable {
@Override
public void run() {
while (true) {
}
}
}
public static void main(String[] args) throws Exception{
// sleepThread不斷的嘗試睡眠
Thread sleepThread = new Thread(new SleepRunner(), "sleepThread");
/**
* Daemon執行緒是一種支持執行緒,主要被用作程式中后臺調度以及支持性作業,
* 當一個Java虛擬機總不存在非Daemon時,Java虛擬機將會退出,
*/
sleepThread.setDaemon(true);
// busy執行緒不停地運行
Thread busyThread = new Thread(new BusyRunner(), "busyThread");
busyThread.setDaemon(true);
sleepThread.start();
busyThread.start();
// 休眠5秒,讓sleep執行緒和busy執行緒充分運行
SleepUtils.second(5);
sleepThread.interrupt();
busyThread.interrupt();
System.out.println("sleepThread interrupted is " + sleepThread.isInterrupted());
System.out.println("busyThread interrupted is " + busyThread.isInterrupted());
// 防止兩個執行緒like退出
SleepUtils.second(2);
}
}
運行結果
sleepThread interrupted is false
busyThread interrupted is true
從結果看出,拋出InterruptException的執行緒是SleepThread,其中標識位被清除了,而一直作業的BusyThread執行緒中斷標識位并沒有被清除,
1.4 過期的suspend()、resume()和stop()
如果把一個執行緒的運行比作播放音樂的MP3的話,那么對音樂做出暫停、恢復和停止操作對應在執行緒Thread的API就是suspend()、resume()和stop(),
我們創建一個執行緒PrintThread,讓它以一秒的頻率進行列印,主執行緒對其進行暫停、恢復和停止操作,
public class Deprecated {
static class Runner implements Runnable{
@Override
public void run() {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
while (true) {
System.out.println(Thread.currentThread().getName() + "run at " + format.format(new Date()));
SleepUtils.second(1);
}
}
}
public static void main(String[] args) throws Exception {
DateFormat format = new SimpleDateFormat("HH:mm:ss");
Thread printThread = new Thread(new Runner(), "PrintThread");
printThread.setDaemon(true);
printThread.start();
TimeUnit.SECONDS.sleep(3);
// 將printThread進行暫停,輸出內容作業停止
printThread.suspend();
System.out.println("主執行緒暫停列印執行緒:" + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
// 將printThread進行恢復,繼續輸出內容
printThread.resume();
System.out.println("主執行緒恢復列印執行緒:" + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
// 將printThread進行恢復,繼續輸出內容
printThread.stop();
System.out.println("主執行緒停止列印執行緒:" + format.format(new Date()));
TimeUnit.SECONDS.sleep(3);
}
}
運行結果
PrintThreadrun at 00:24:26
PrintThreadrun at 00:24:27
主執行緒暫停列印執行緒:00:24:28
主執行緒恢復列印執行緒:00:24:31
PrintThreadrun at 00:24:31
PrintThreadrun at 00:24:32
PrintThreadrun at 00:24:33
主執行緒停止列印執行緒:00:24:34
在執行程序中,PrintThread運行了3秒,隨后被主執行緒暫停,3秒后恢復,最后運行3秒被終止,雖然這些API很“人性化”,但是這些這些API是過期的,也不建議使用,
不建議使用的原因有:以suspend()方法為例,在呼叫后執行緒不會釋放已經占有的鎖,而是占有者資源進入睡眠狀態,這用容易引發死鎖問題,同樣stop()方法在終止一個執行緒時不會保證執行緒的資源是正常釋放,一般是沒有給執行緒釋放資源的機會,因此會導致程式可能作業在不確定狀態下,正因為這些方法帶來的副作用,這些方法才會被標記為過時的,而暫停和恢復操作可以等待/通知機制來代替,
1.5 安全的終止執行緒
在1.3節中提到的中斷狀態是執行緒的一個標識位,而中斷操作是一種簡單的執行緒間互動方式,這種互動方式最適合用來取消或停止任務,而除了中斷以外,還可以用一個boolean變數來控制是否需要停止任務或終止執行緒,
創建一個CountThread,讓它不斷的進行變數累加,主執行緒嘗試對其進行中斷和停止操作
public class CountThread {
private static class Runner implements Runnable{
private int i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("i = " + i);
}
public void cancel() {
on = false;
}
}
public static void main(String[] args) {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main執行緒對CountThread進行中斷,使CountThread能夠感知中斷而結束
SleepUtils.second(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main執行緒對Runner two進行取消,使CountThread能夠感知on為false而結束
SleepUtils.second(1);
two.cancel();
}
}
運行結果
i = 86559055
i = 90464710
有實體代碼可以看出,在執行程序中,mian執行緒通過中斷操作和cancel()方法均可使CountThread終止,這種通過標識位或者中斷操作的方式能夠使執行緒在終止時有機會去清理資源,而不是武斷的去停止執行緒,
結尾
這一篇講了執行緒的構造、啟動、中斷操作、一些過期的API以及安全地終止執行緒,重點是對執行緒中斷的理解,歡迎大家積極留言評論,如果對你幫助的話不妨點個贊加個收藏,學習之長路漫漫,吾將上下而求索,共勉之,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/276977.html
標籤:其他
