摘要:前言本文描述線程線程狀態及狀態轉換,不會涉及過多理論,主要以代碼示例說明線程狀態如何轉換。被終止線程執行完畢正常結束或執行過程中因未捕獲異常意外終止都會是線程進入被終止狀態。線程執行完畢打印狀態。
前言
本文描述Java線程線程狀態及狀態轉換,不會涉及過多理論,主要以代碼示例說明線程狀態如何轉換。
基礎知識 1. 線程狀態線程可以有6種狀態:
New(新建)
Runnable(可運行)
Blocked(被阻塞)
Waiting(等待)
Timed waiting(計時等待)
Terminated(被終止)
New:new Thread()后線程的狀態就是新建。
Runnable:線程一旦調用start()方法,無論是否運行,狀態都為Runable,注意Runable狀態指示表示線程可以運行,不表示線程當下一定在運行,線程是否運行由虛擬機所在操作系統調度決定。
被阻塞:線程試圖獲取一個內部對象的Monitor(進入synchronized方法或synchronized塊)但是其他線程已經搶先獲取,那此線程被阻塞,知道其他線程釋放Monitor并且線程調度器允許當前線程獲取到Monitor,此線程就恢復到可運行狀態。
等待:當一個線程等待另一個線程通知調度器一個條件時,線程進入等待狀態。
計時等待:和等待類似,某些造成等待的方法會允許傳入超時參數,這類方法會造成計時等待,收到其他線程的通知或者超時都會恢復到可運行狀態。
被終止:線程執行完畢正常結束或執行過程中因未捕獲異常意外終止都會是線程進入被終止狀態。
2. 線程狀態轉換線程從“新建”到“被終止”會歷經多次狀態轉換,所有可能的轉換如下圖:
【圖一】
觀察狀態轉化圖,我們發現“可運行”狀態為所有狀態的必經狀態。我們分析出四條基本的狀態轉換線路圖。
新建--->可運行--->被終止
新建--->可運行--->被阻塞--->可運行--->被終止
新建--->可運行--->等待--->可運行--->被終止
新建--->可運行--->計時等待--->可運行--->被終止
“新建”和“被終止”狀態分別為起始和結束狀態,和“可運行”狀態不可逆。其他狀態均能和“可運行”狀態相互轉換。
用代碼說話讓我們用代碼演示線程狀態是如何轉換的,大家重點關注兩個問題?
什么操作會改變線程狀態?
改變的狀態是如何恢復的?
一、 新建--->可運行--->被終止這個狀態轉換時創建的線程生命周期。
/** * NEW->RUNNABLE->TERMINATED */ public class ThreadStateNRT { public static void main(String[] args) { Thread thread=new Thread(new Task()); print(thread.getName(),thread.getState()); thread.start(); //等待線程執行完畢。 try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } print(thread.getName(),thread.getState()); } private static class Task implements Runnable{ @Override public void run() { print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } private static final String stringFormat="%s:%s"; private static void print(String threadName,Thread.State state){ System.out.println(String.format(stringFormat,threadName,state)); } }
其中,print()方法用來打印線程信息。后面的代碼示例均不在展示。
運行程序結果為:
Thread-0:NEW二、 新建--->可運行--->被阻塞--->可運行--->被終止
Thread-0:RUNNABLE
Thread-0:TERMINATED
只有一種方法能出現阻塞狀態,那就是synchronized同步原語。我們需要兩個線程其中一個線程被另一個阻塞來展示。
/** * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED */ public class ThreadStateNRBRT { //鎖 private static final Object lock=new Object(); public static void main(String[] args) { //輔助線程,制造synchronized狀態。 Thread assistantThread = new Thread(new SynTask()); assistantThread.start(); try { //保證assistantThread先執行。 Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Thread showThread = new Thread(new Task()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(),showThread.getState()); //因為無法判斷顯示線程何時執行,所以循環直到顯示線程執行。 while (true){ if(showThread.getState()==Thread.State.BLOCKED){ print(showThread.getName(), Thread.State.BLOCKED); break; } } //等待兩個線程執行完畢。 try { assistantThread.join(); showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class SynTask implements Runnable { @Override public void run() { //鎖定一定時間 synchronized (lock){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } private static class Task implements Runnable { @Override public void run() { synchronized (lock){ print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } } }
執行一下你有可能看到正確結果:
Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:BLOCKED
Thread-1:RUNNABLE
Thread-1:TERMINATED
為什么是有可能呢?我們調整一下代碼,例如將加鎖的時間調小一點:
private static class SynTask implements Runnable { @Override public void run() { //鎖定一定時間 synchronized (lock){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意此處Thread.sleep(10),我們只鎖住十毫秒。
再運行一下,控制臺可能打印出這樣的結果且程序不會結束:
Thread-1:NEW
Thread-1:RUNNABLE
Thread-1:RUNNABLE
造成以上結果的原因是我么無法保證兩個線程的執行順序,也無法證主線程一定能打印出顯示線程阻塞的狀態。
while (true){ if(showThread.getState()==Thread.State.BLOCKED){ print(showThread.getName(), Thread.State.BLOCKED); break; } }
所以執行在這段代碼死循環了。
調整一下代碼,保證不會因為參數調整改變線程之間的執行順序和打印結果。
/** * NEW->RUNNABLE->BLOCKED->RUNNABLE->TERMINATED */ public class ThreadStateNRBRT_New { //鎖 private static final Object lock=new Object(); //鎖定標志 private volatile static boolean lockFlag=true; //執行順序 private volatile static int order=0; public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new Task()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(), showThread.getState()); //輔助線程,制造synchronized狀態。 Thread assistantThread = new Thread(new SynTask()); assistantThread.start(); //循環讀取展示線程狀態,直到讀到展示線程狀態為BLOCKED,才讓輔助線程退出同步。 while (true){ if(showThread.getState()==Thread.State.BLOCKED){ print(showThread.getName(), Thread.State.BLOCKED); lockFlag=false; break; } } try { assistantThread.join(); showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class SynTask implements Runnable { @Override public void run() { while (true) { //保證先進入同步范圍。 if (order == 0) { synchronized (lock) { //啟動另一個同步 order=1; //等待主線程讀取到線程阻塞狀態,退出同步。 while (lockFlag) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } break; } } } } private static class Task implements Runnable { @Override public void run() { while (true){ //保證后進入同步范圍。 if (order==1){ synchronized (lock){ print(Thread.currentThread().getName(),Thread.currentThread().getState()); } break; } } } } }
我們用order保證線程進入同步區的順序,用lockFlag保證只有在打印出顯示線程的被阻塞狀態后輔助線程才退出同步區。這樣無論如何執行我們都會得到同樣的結果。
Thread-0:NEW三、 新建--->可運行--->等待--->可運行--->被終止
Thread-0:RUNNABLE
Thread-0:BLOCKED
Thread-0:RUNNABLE
Thread-0:TERMINATED
這里我們展示兩種三種方法造成線程的等待狀態
Object.wait()
java.util.concurrent.locks.Locke.lock()
java.util.concurrent.locks.Condition.await()
其他方法如Thread.join()等大家可以參考示例代碼自己實現。
1. Object.wait()/** * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED */ public class ThreadStateNRWRT { //鎖 private static final Object lock=new Object(); public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new WaitTask()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(),showThread.getState()); //循環讀取展示線程狀態,直到讀到展示線程狀態為WAITING,才讓輔助線程喚醒等待線程。 while (true){ if(showThread.getState()==Thread.State.WAITING){ print(showThread.getName(), Thread.State.WAITING); break; } } synchronized (lock){ lock.notify(); } try { showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class WaitTask implements Runnable { @Override public void run() { //等待 synchronized (lock){ try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } }2. java.util.concurrent.locks.Locke.lock()
/** * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED */ public class ThreadStateNRWRTLock { //鎖 private static Lock lock=new ReentrantLock(); //鎖定標志 private volatile static boolean lockFlag=true; //執行順序 private volatile static int order=0; public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new Task()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(), showThread.getState()); //輔助線程,制造synchronized狀態。 Thread assistantThread = new Thread(new SynTask()); assistantThread.start(); //循環讀取展示線程狀態,直到讀到展示線程狀態為BLOCKED,才讓輔助線程退出同步。 while (true){ if(showThread.getState()==Thread.State.WAITING){ print(showThread.getName(), Thread.State.WAITING); lockFlag=false; break; } } try { assistantThread.join(); showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class SynTask implements Runnable { @Override public void run() { while (true) { //保證先進入同步范圍。 if (order == 0) { //加鎖 lock.lock(); try { order=1; while (lockFlag) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }finally { lock.unlock(); } break; } } } } private static class Task implements Runnable { @Override public void run() { while (true){ //保證后進入同步范圍。 if (order==1){ lock.lock(); try{ print(Thread.currentThread().getName(),Thread.currentThread().getState()); }finally { lock.unlock(); } break; } } } } }3. java.util.concurrent.locks.Condition.await()
/** * NEW->RUNNABLE->WAITING->RUNNABLE->TERMINATED */ public class ThreadStateNRWRTCondition { //鎖 private static Lock lock=new ReentrantLock(); private static Condition condition=lock.newCondition(); public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new WaitTask()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(),showThread.getState()); //循環讀取展示線程狀態,直到讀到展示線程狀態為WAITING,才讓輔助線程喚醒等待線程。 while (true){ if(showThread.getState()==Thread.State.WAITING){ print(showThread.getName(), Thread.State.WAITING); break; } } lock.lock(); try{ condition.signal(); }finally { lock.unlock(); } try { showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class WaitTask implements Runnable { @Override public void run() { //等待 lock.lock(); try{ try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } }finally { lock.unlock(); } print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } }4. 運行結果
三段代碼的運行結果都是:
Thread-0:NEW四、新建--->可運行--->計時等待--->可運行--->被終止
Thread-0:RUNNABLE
Thread-0:WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED
我們展示兩個方法造成計時等待狀態
Object.wait(long timeout)
java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)
其他方法如Thread.sleep(long millis),Thread.join(long millis)等大家可以自己實現。
感覺凡是有超時方法的方法都能讓線程狀態進入計時等待,但是這個沒有經過驗證,所以只是一個猜想。
/** * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED */ public class ThreadStateNRTWRT { //鎖 private static final Object lock=new Object(); public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new WaitTask()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(),showThread.getState()); //循環讀取展示線程狀態,直到讀到展示線程狀態為TIMED_WAITING。 while (true){ if(showThread.getState()==Thread.State.TIMED_WAITING){ print(showThread.getName(), Thread.State.TIMED_WAITING); break; } } try { showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class WaitTask implements Runnable { @Override public void run() { //等待 synchronized (lock){ try { lock.wait(1); } catch (InterruptedException e) { e.printStackTrace(); } } print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } }2. java.util.concurrent.locks.Condition.await(long time, TimeUnit unit)
/** * NEW->RUNNABLE->TIMED_WAITING->RUNNABLE->TERMINATED */ public class ThreadStateNRTWRTCondition { //鎖 private static Lock lock=new ReentrantLock(); private static Condition condition=lock.newCondition(); public static void main(String[] args) { //展示線程 Thread showThread = new Thread(new WaitTask()); print(showThread.getName(), showThread.getState()); showThread.start(); print(showThread.getName(),showThread.getState()); //循環讀取展示線程狀態,直到讀到展示線程狀態為TIMED_WAITING。 while (true){ if(Thread.State.TIMED_WAITING==showThread.getState()){ print(showThread.getName(), Thread.State.TIMED_WAITING); break; } } try { showThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程執行完畢打印狀態。 print(showThread.getName(), showThread.getState()); } private static class WaitTask implements Runnable { @Override public void run() { //等待 lock.lock(); try{ try { condition.await(1,TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } }finally { lock.unlock(); } print(Thread.currentThread().getName(),Thread.currentThread().getState()); } } }3. 運行結果
兩段程序的運行結果相同:
Thread-0:NEW結語
Thread-0:RUNNABLE
Thread-0:TIMED_WAITING
Thread-0:RUNNABLE
Thread-0:TERMINATED
至此,我們已經介紹了線程狀態轉換的所有情況,了解線程狀態轉換對分析多線程代碼運行很幫助。希望本篇文章對大家今后工作有所助力。
參考《Java核心技術+卷1》第九版
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76926.html
摘要:一無鎖方案并發包中的原子類都是基于無鎖方案實現的,相較于傳統的互斥鎖,無鎖并沒有加鎖解鎖線程切換的消耗,因此無鎖解決方案的性能更好,同時無鎖還能夠保證線程安全。線程首先讀取的值并加,如果此時有另一個線程更新了,則期望值和不相等,更新失敗。 一、無鎖方案 Java 并發包中的原子類都是基于無鎖方案實現的,相較于傳統的互斥鎖,無鎖并沒有加鎖、解鎖、線程切換的消耗,因此無鎖解決方案的性能更好...
摘要:對象存不進去,會又一次觸發垃圾回收。也就是說,它在進行垃圾回收時,必須暫停其他所有線程。我們來看一個名詞吞吐量。吞吐量運行用戶代碼時間運行用戶代碼時間垃圾收集時間。也就是說,收集器會嚴格控制吞吐量,至于這個吞吐量是多少,這個可以人為設置。 與其他語言相比,例如c/c++,我們都知道,java虛擬機對于程序中產生的垃圾,虛擬機是會自動幫我們進行清除管理的,而像c/c++這些語言平臺則需要...
摘要:本文將討論兩種可用于解決貝葉斯推理問題的主要方法基于采樣的馬爾可夫鏈蒙特卡羅,簡稱方法和基于近似的變分推理,簡稱方法。而貝葉斯推理則是從貝葉斯的角度產生統計推斷的過程。貝葉斯推理問題還可能會產生一些其他的計算困難。 全文共6415字,預計學習時長20分鐘或更長 showImg(https://segmentfault.com/img/bVbvFZZ?w=1280&h=853); 圖片來...
閱讀 1079·2021-11-16 11:44
閱讀 1368·2019-08-30 13:12
閱讀 2401·2019-08-29 16:05
閱讀 3070·2019-08-28 18:29
閱讀 904·2019-08-26 13:41
閱讀 3228·2019-08-26 13:34
閱讀 2596·2019-08-26 10:35
閱讀 931·2019-08-26 10:28