Java多執行緒(二)執行緒排程
阿新 • • 發佈:2022-03-03
Java多執行緒(二)
1.執行緒的生命週期及狀態轉換
六種狀態:
- NEW(新建狀態):建立一個執行緒後,該執行緒物件就處於新建狀態,此時它不能執行,僅僅由JVM為其分配了記憶體,沒有表現出任何執行緒的動態特徵。
- RUNNABLE(可執行狀態):新建狀態的執行緒呼叫start()方法就會進入可執行狀態。在RUNNABLE狀態又可細分成兩種狀態:READY(就緒狀態)和RUNNING(執行狀態),並且執行緒可以在二者間轉換。
- 就緒狀態:呼叫start()方法後等待JVM排程。
- 執行狀態:執行緒物件獲得JVM排程,若存在多個CPU,那麼允許多個執行緒並行執行。
- BLOCKED(阻塞狀態):處於執行狀態的執行緒可能會因為某些原因失去CPU執行權,暫時停止執行進入阻塞狀態。此時JVM不會給執行緒分配CPU,直到執行緒重新進入就緒狀態,才有機會轉換到執行狀態。一般以下兩種情況會進入阻塞狀態:
- 執行緒A執行中,試圖獲取同步鎖時,卻被執行緒B獲取,此時JVM把執行緒A存到物件的鎖池中。
- 執行緒執行過程中發出I/O請求時,此時該執行緒也會進入阻塞狀態。
- WAITING(等待狀態):將執行狀態的執行緒呼叫了無時間引數限制的方法後,如:wait()、join()等方法,就會將當前執行中的執行緒轉換為等待狀態。處於該狀態時,必須等其他執行緒執行特定操作後,才有機會再次爭奪CPU使用權,將等待狀態的執行緒轉換為執行狀態。
- TIMED_WAITING(定時等待狀態):將執行狀態的執行緒轉換為定時等待狀態與轉換為等待狀態操作類似,只是執行執行緒呼叫了有時間引數限制的方法。
- TERMINATED(終止狀態):執行緒的run()方法、call()方法正常執行完畢或執行緒丟擲了一個未捕獲的異常、錯誤,執行緒就進入終止狀態。進入終止狀態,執行緒不在擁有執行資格,也不能轉換到其他狀態,生命週期結束。
2.執行緒的排程
執行緒排程的兩種模型:
- 分時排程模型:讓所有執行緒輪流獲得CPU使用權。
- 搶佔式排程模型:讓可執行池中所有就緒狀態的執行緒搶佔CPU的使用權,優先順序高的執行緒獲得CPU執行權的概率較大。
執行緒優先順序
對執行緒進行排程,最直接的方法是設定執行緒優先順序,用1~10之間的整數來表示,數字越大優先順序越高。也可以使用Thread類的三個靜態常量表示執行緒優先順序。
static int MAX_PRIORITY //最高優先順序,相當於10 static int MIN_PRIORITY //最低優先順序,相當於1 static int NORM_PRIORIY //普通優先順序,相當於5
程式執行期間,就緒狀態的每個執行緒都有優先順序,如main()具有普通優先順序。執行緒優先順序不是固定不變的,可以通過Thread類的setPriority(int newPriority)方法進行設定。
舉例:
public class Page364 {
public static void main(String[] args) {
Thread thread1=new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"正在輸出i:"+i);
}
},"優先順序較低的執行緒");
Thread thread2=new Thread(()->{
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName()+"正在輸出j:"+j);
}
},"優先順序較高的執行緒");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
}
3.執行緒休眠
使用靜態方法sleep(long millis)方法使執行緒暫停一段時間,這樣其他執行緒就可以得到執行機會。但該方法會丟擲InterruptedException異常,因此呼叫該方法時應捕獲異常或宣告丟擲異常。
舉例:
public class Page365 {
public static void main(String[] args) {
Thread thread1=new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"正在輸出i:"+i);
if(i==2){
try{
Thread.sleep(500);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
});
Thread thread2=new Thread(()->{
for(int j=0;j<10;j++){
System.out.println(Thread.currentThread().getName()+"正在輸出j:"+j);
}
});
thread1.start();
thread2.start();
}
}
4.執行緒讓步
執行緒讓步可以通過yield()方法來實現,與sleep()有點類似,都可以讓正在執行的執行緒暫停,但yield()不會阻塞該執行緒,它只是將執行緒轉換成就緒狀態,讓系統的排程器重新排程一次,,使用yield()後,與當前執行緒優先順序相同或更改的執行緒可以獲得執行機會。
舉例:
class YieldThread extends Thread{
public YieldThread(String name){
super(name);
}
public void run(){
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
if(i==2){
System.out.println("執行緒讓步");
Thread.yield();
}
}
}
}
public class Page367 {
public static void main(String[] args) {
Thread thread1=new YieldThread("thread1");
Thread thread2=new YieldThread("thread2");
thread1.start();
thread2.start();
}
}
5.執行緒插隊
Thread類中提供了join()方法,在某個執行緒中呼叫其他執行緒的join()方法時,呼叫的執行緒將被堵塞,直到被join()方法加入的執行緒執行完成之後它才會繼續執行。
舉例:
class EmergencyThread implements Runnable{
public void run(){
for(int i=1;i<6;i++){
System.out.println(Thread.currentThread().getName()+"輸入:"+i);
}
}
}
public class Page368 {
public static void main(String[] args) throws InterruptedException{
Thread thread1=new Thread(new EmergencyThread(),"thread1");
thread1.start();
for(int i=1;i<6;i++){
System.out.println(Thread.currentThread().getName()+"輸入:"+i);
if(i==2){
thread1.join();
}
}
}
}