1、Java多線程與並發,進程與線程的區別。
答:進程是資源分配的最小單位,線程是CPU調度的最小單位。
1)、進程是資源分配的基本單位,所有與進行相關的資源,都被記錄在進程控制塊PCB中,以表示該進程擁有這些資源或者正在使用它們。
2)、進程是搶占處理機的調度單位,線程屬於某個進程,共享其資源。進程擁有一個完整的虛擬內存地址空間,當進程發生調度的時候,不同的進程擁有不同的虛擬地址空間,而同一進程內不同線程共享同一地址空間,與進程相對應。線程與資源分配無關,它屬於某一個進程,並與進程內的其它線程一起共享進程里面的資源。
3)、線程只由堆棧、寄存器、程序計數器和線程計數表TCB組成。
2、進程與線程的區別總結。
1)、線程不能看做獨立應用,而進程可看做獨立應用。操作系統並沒有將多個線程看作多個獨立的應用來實現進程的調度和管理以及資源分配。
2)、進程有獨立的地址空間,一個進程奔潰后,在保護模式下,不會對其他進程產生影響,相互不影響,線程只是進程的不同指向路徑,如果某個線程掛掉,那么它所在的進程也會掛掉。
3)、線程有自己的堆棧和局部變量,但線程沒有獨立的地址空間,多進程的程序比多線程程序健壯。
4)、進程的切換比線程的切換開銷大,效率差很多,如果要求同時進行並且又要共享某些變量的並發操作,只能用線程,不能用進程,每個獨立的線程有個程序運行的入口,順序執行序列和程序的出口,但是線程不能獨立執行,必須依存於某一個應用程序當中,由應用程序提供對多個線程的執行控制。
3、Java進程與線程的關系。
答:1)、Java對操作系統提供的功能進行封裝,包括進程和線程。
2)、運行一個程序會產生一個進程,進程包含至少一個線程。
3)、每個進程對應一個JVM實例,多個線程共享JVM里面的堆,每個JVM實例唯一對應一個堆,每一個線程都有自己私有的棧。
4)、Java采用單線程編程模型,程序會自動創建主線程,自己的程序中如果沒有主動創建線程的話,程序會自動創建一個線程,這個線程就是主線程,因此在編程的時候,將耗時的操作放入子線程中進行,以避免阻塞主線程,影響用戶體驗。
5)、Java程序啟動的時候,主線程立刻運行,主線程的重要性體現在,主線程可以創建子線程,原則上要后於子線程完成執行,主線程通常是最后完成執行,因為它需要執行各種關閉動作。
3、Java線程的start和run方法的區別?
答:1)、調用start()方法會創建一個新的子線程並啟動。
2)、run()方法只是Thread的一個普通方法的調用。
4、Java線程的Thread和Runnable的關系?
答:1)、Thread是一個類,Runnable是一個接口,Thread實現了Runnable接口。
2)、Thread是實現了Runnable接口的類,使得run支持多線程。
3)、因為Java類的單一繼承原則,推薦多使用Runnable接口的方式。
5、那么如何給java多線程的run()方法傳參呢。實現的方式主要有三種。
答:1)、構造函數傳參。
2)、成員變量傳參,通過set方法進行傳參。
3)、回調函數傳參。
6、Java線程中如何實現處理線程的返回值。
答:和線程相關的業務邏輯需要放入到run()方法里面,但是run方法是沒有參數的,並且也沒有返回值的,那么如何給run()方法傳參呢。有的程序的執行是依賴於子任務的返回值進行的,當子任務交給子線程去完成的時候,是需要獲取到它們的返回值的,此時如何獲取到子線程的返回值呢。實現的方式主要有三種。
1)、主線程等待法,即讓主線程循環等待,直到目標子線程返回值為止,主線程等待法實現簡單,缺點是需要自己實現循環等待的邏輯,但是如果等待的變量一多,代碼就會顯得異常的臃腫,而且需要循環多久是不確定的,無法左到精准的控制。
2)、使用Thread類的join()阻塞當前線程以等待子線程處理完畢,join方法可以阻塞調用此方法的線程即這里可以阻塞主線程,直到join方法所在的線程執行完畢為止,此方法比主線程等待法做到更精准的控制,實現起來簡單,缺點是粒度不夠細。
3)、通過Callable接口實現,通過FutureTask Or線程池獲取。JDK5之后新增了Callable接口,執行了Callable任務之后,可以獲取一個Future的對象,在該對象上調用get方法就可以獲取到Callable任務返回的對象。關於通過Callable接口實現的方式有兩種方式來實現,第一種是通過FutureTask,第二種是線程池獲取。
代碼案例,主線程等待法、使用線程Thead類的join()方法的實現,如下所示:
1 package com.thread; 2 3 public class CycleWait implements Runnable { 4 5 // 私有成員變量 6 private String value; 7 8 @Override 9 public void run() { 10 try { 11 // 子線程休眠5秒鍾 12 Thread.currentThread().sleep(5000); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 // 通過run方法給value賦值 17 value = "we have data now"; 18 } 19 20 public static void main(String[] args) { 21 CycleWait cycleWait = new CycleWait(); 22 Thread thread = new Thread(cycleWait); 23 thread.start(); 24 // 方案一,主線程等待法 25 // while (cycleWait.value == null) { 26 // try { 27 // // 子線程休眠0.1秒鍾 28 // Thread.currentThread().sleep(100); 29 // } catch (InterruptedException e) { 30 // e.printStackTrace(); 31 // } 32 // } 33 34 35 // 方案二,使用Thread類的join()阻塞當前線程以等待子線程處理完畢 36 try { 37 thread.join(); 38 } catch (InterruptedException e) { 39 e.printStackTrace(); 40 } 41 42 43 // 如果不使用主線程等待法,是無法打印子線程的返回值的 44 System.out.println("value : " + cycleWait.value); 45 } 46 }
關於通過Callable接口實現的方式有兩種方式來實現,第一種是通過FutureTask,第二種是線程池獲取。
1 package com.thread; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.FutureTask; 6 7 public class MyCallable implements Callable<String> { 8 9 @Override 10 public String call() throws Exception { 11 String value = "實現Callable接口的多線程"; 12 System.out.println("Ready to work." + value); 13 // 休眠5秒 14 Thread.currentThread().sleep(5000); 15 System.out.println("task done."); 16 int sum = 0; 17 for (int i = 0; i < 100; i++) { 18 sum = sum + i; 19 } 20 return sum + ""; 21 } 22 23 public static void main(String[] args) { 24 // 第一種方式,FutureTask的構造函數可以接收Callable實現類的實例的 25 FutureTask<String> futureTask = new FutureTask<String>(new MyCallable()); 26 // FutureTask<V> implements RunnableFuture<V> 27 // RunnableFuture<V> extends Runnable, Future<V> 28 Thread thread = new Thread(futureTask); 29 // 啟動線程 30 thread.start(); 31 32 // 判斷子線程是否執行完畢,isDone()方法用來判斷傳遞給FutureTask的Callable實現類是否已經執行完畢。 33 if (!futureTask.isDone()) { 34 System.out.println("task has not finished,please wait."); 35 } 36 // 獲取到子線程返回的值,無參的get()方法主要用來阻塞當前調用它的線程,直到我們的Callable實現類的call方法執行完畢為止 37 // 然后取到返回值,可以精准的獲取到子線程處理完畢之后的返回值的。 38 // 帶參的get()方法,可以傳入等待時間,是一個超時機制,超時時間內還沒有獲取到call方法的返回值的話就拋出異常。 39 try { 40 System.out.println("task return : " + futureTask.get()); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } catch (ExecutionException e) { 44 e.printStackTrace(); 45 } 46 47 } 48 49 }
1 package com.thread; 2 3 import java.util.concurrent.ExecutionException; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 import java.util.concurrent.Future; 7 8 public class ThreadPollDemo { 9 10 public static void main(String[] args) { 11 // 創建線程池,使用線程池的好處可以提交多個實現Callable接口的類,讓線程池並發的處理結果, 12 // 方便對實現了Callable接口的類進行管理 13 ExecutorService newCachedThreadPoll = Executors.newCachedThreadPool(); 14 // 向線程池提交任務。submit接收Callable接口 15 Future<String> future = newCachedThreadPoll.submit(new MyCallable()); 16 // FutureTask<V> implements RunnableFuture<V> 17 // RunnableFuture<V> extends Runnable, Future<V> 18 if (!future.isDone()) { 19 System.out.println("task has not finished,please wait."); 20 } 21 try { 22 System.out.println("task return : " + future.get()); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } catch (ExecutionException e) { 26 e.printStackTrace(); 27 } finally { 28 // 關閉線程池 29 newCachedThreadPoll.shutdown(); 30 } 31 } 32 }
7、Java線程中線程的狀態。
答:線程的狀態有六個,新建New狀態、運行Runnable狀態、無線期等待Waiting、限期等待Timed Waiting、阻塞 Blocked、結束Terminated狀態。
1)、新建New狀態,創建后尚未啟動的線程的狀態,即新創建了一個線程,但是還沒有調用start方法。
2)、運行Runnable狀態,包含Running和Ready。包括操作系統的Running和ready,也就是處於此狀態的線程有可能正在執行,也有可能正在等待着CPU為它分配執行時間,比如線程對象創建后,調用了該對象的start方法之后,這個時候線程處於Runnable狀態,由於該狀態分為兩個子狀態Running和Ready,處於Running的線程位於可運行線程之中,等待被線程調度選中,獲取CPU的使用權,處於Ready狀態的線程位於線程池中,等待被線程調度選中,獲取CPU的使用權,而處於Ready狀態的線程在獲得CPU時間后,就變為Running狀態的線程。
3)、無線期等待Waiting,不會被分配CPU執行時間,需要顯式被喚醒,需要其它線程顯式的喚醒。以下方法會讓線程陷入無線期等待中。
a)、沒有設置Timeout參數的Object.wait()方法。
b)、沒有設置Timeout參數的Thread.join()方法。
c)、LockSupport.park()方法。
4)、限期等待Timed Waiting,處於這種狀態的線程,不會被分配CPU執行時間,不過無需等待其它線程顯式喚醒,在一定時間后會由系統自動喚醒。
a)、Thread.sleep()方法。
b)、設置Timeout參數的Object.wait()方法。
c)、設置Timeout參數的Thread.join()方法。
d)、LockSupport.parkNanos()方法。
e)、LockSupport.parkUntil()方法。
5)、阻塞 Blocked,等待獲取排它鎖。阻塞狀態和等待狀態的區別是,阻塞狀態在等待着獲取到一個排它鎖,這個事件將在另外一個線程中放棄這個鎖的時候發生,而等待狀態則是在等待一段時間或者有喚醒動作的時候發生,在程序等待進入同步區域的時候,線程將進入Blocked狀態。比如,當某個線程進入synchronized關鍵字修飾的方法或者代碼塊的時候,即獲取鎖執行的時候,其它想進入此方法或者代碼塊的線程就只能等着,它們的狀態便是Blocked
6)、結束Terminated狀態,已終止線程的狀態,線程已經結束執行。讓線程的run方法完成的時候,或者主線程的main方法完成的時候,我們就認為它終止了,這個線程對象也許是活的,但是它已經不是一個單獨執行的線程,線程一旦終止了,就不能再復生,在一個終止的線程調用start方法會拋出異常。
8、Java線程中sleep和wait的區別?
答:sleep方法和wait方法的基本的差別。
1)、sleep()方法是Thread類的方法,wait()方法是Object類中定義的方法。
2)、sleep()方法可以在任何地方使用。
3)、wait()方法只能在synchronized方法或者synchronized塊中使用。
sleep方法和wait方法的最本質的差別。
1)、Thread.sleep()方法只會讓出CPU,不會導致鎖行為的變化。如果當前線程是擁有鎖的,那么Thread.sleep()方法不會讓線程釋放鎖,而只會主動讓出CPU,讓出CPU之后呢,CPU就可以去執行其它任務了。
2)、Object.wait()方法不僅讓出CPU,還會釋放已經占有的同步資源鎖,以便其它正在等待該資源的線程得到該資源進而去運行。
1 package com.thread; 2 3 public class WaitSleepDemo { 4 5 public static void main(String[] args) { 6 // 創建一個不可變的Object對象 7 final Object lock = new Object(); 8 new Thread(new Runnable() { 9 10 @Override 11 public void run() { 12 // 線程A等待獲取鎖lock 13 System.out.println("thread A is waiting to get lock."); 14 // 獲取同步鎖才可以執行代碼塊里面的邏輯 15 synchronized (lock) { 16 try { 17 // 獲取到了鎖lock 18 System.out.println("thread A get lock."); 19 // 模擬程序執行 20 Thread.sleep(20); 21 // 開始調用wait的方法 22 System.out.println("Thead A do wait method."); 23 // 調用wait方法如果不傳入參數就進入無限期等待 24 // 如果傳入1000就等待一秒自動被喚醒,進入限期等待狀態。 25 // lock.wait(1000); 26 27 // 此時,將A線程和B線程的wait方法和sleep方法反過來 28 Thread.sleep(1000);// sleep方法不會釋放同步鎖的。 29 System.out.println("Thread A is done."); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 } 34 } 35 }).start(); 36 37 // 為了顯式出效果,此處休眠10秒鍾。 38 try { 39 Thread.sleep(10); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 44 new Thread(new Runnable() { 45 46 @Override 47 public void run() { 48 // 線程B等待獲取鎖lock 49 System.out.println("thread B is waiting to get lock."); 50 // 獲取同步鎖才可以執行代碼塊里面的邏輯 51 synchronized (lock) { 52 try { 53 // 獲取到了鎖lock 54 System.out.println("thread B get lock."); 55 System.out.println("Thead B is sleeping 10 ms."); 56 // 模擬程序執行 57 // Thread.sleep(10); 58 59 // 此時,將A線程和B線程的wait方法和sleep方法反過來 60 lock.wait();//讓出CPU,釋放同步鎖 61 System.out.println("Thread B is done."); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 } 66 } 67 }).start(); 68 69 } 70 71 72 }
9、Java線程中鎖池EntryList和等待池WaitSet的區別?
答:先了解兩個概念,對於java虛擬機中,運行程序的每一個對象來說,都有兩個池,鎖池EntryList、等待池WaitSet,而這兩個吃又與Object基類的wait,notify,notifyAll三個方法,以及synchronized相關。
1)、鎖池EntryList,假設線程A已經擁有了某個對象(不是類)的鎖,而其它線程B、C想要調用這個對象的某個synchronized方法(或者塊),由於B、C線程在進入對象的synchronized方法(或者塊)之前必須先獲得該對象鎖的擁有權,而恰巧該對象的鎖目前正被線程A所占用,此時B、C線程就會被阻塞,進入一個地方去等待鎖的釋放,這個地方便是該對象的鎖池,就是將B、C加入到鎖池里面。
2)、等待池WaitSet,假設線程A調用了某個對象的wait方法,線程A就會釋放該對象的鎖,同時線程A就進入到了該對象的等待池中,進入該等待池中的線程不會去競爭該對象的鎖,如果線程B執行完之后調用了notify和notifyall方法的話,則處於該對象等待池中的被喚醒的線程A就會進入到該對象lock鎖池中,鎖池中的對象就會競爭該對象的鎖,如果線程B執行完之后,就會將鎖自動的釋放掉,因此線程A就獲得到了鎖,但是真實的開發中,多個線程去競爭這個鎖,優先級高的線程競爭到這個鎖的機率更大,假如某個線程沒有競爭到該對象鎖,它只會留在鎖池中,並不會重新進入到等待池中,而競爭到該對象鎖的線程繼續向下執行業務邏輯,直到執行完了synchronized方法(或者塊)或者遇到了異常才會釋放掉該對象鎖,這時,鎖池中的線程會繼續競爭該對象鎖。
10、Java線程中 notify 和 notifyall 的區別?
答:1)、notifyall會讓所有處於等待池的線程全部進入鎖池去競爭獲取鎖的機會。沒有獲取到鎖的而已經呆在鎖池中的線程只能等待其它機會,去獲取鎖,而不能再次回到等待池中。
2)、notify只會隨機選取一個處於等待池中的線程進入鎖池去競爭獲取鎖的機會。
1 package com.thread; 2 3 public class NotificationDemo { 4 5 // 成員變量,volatile修飾的成員變量,表示的是多個線程對其進行修改的時候,一旦線程A對其進行修改 6 // 其它線程都可以立即看到線程A對它的改動。 7 private volatile boolean go = false; 8 9 public static void main(String args[]) throws InterruptedException { 10 11 // 創建一個不可變的對象實例 12 final NotificationDemo notificationDemo = new NotificationDemo(); 13 14 // 等待線程,使線程進入等待狀態 15 Runnable waitTask = new Runnable() { 16 17 @Override 18 public void run() { 19 try { 20 notificationDemo.shouldGo(); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 System.out.println(Thread.currentThread().getName() + " finished Execution"); 25 } 26 }; 27 28 29 // 喚醒線程的線程 30 Runnable notifyTask = new Runnable() { 31 32 @Override 33 public void run() { 34 notificationDemo.go(); 35 System.out.println(Thread.currentThread().getName() + " finished Execution"); 36 } 37 }; 38 39 40 // 創建四個線程 41 Thread t1 = new Thread(waitTask, "WT1"); //will wait等待線程 42 Thread t2 = new Thread(waitTask, "WT2"); //will wait等待線程 43 Thread t3 = new Thread(waitTask, "WT3"); //will wait等待線程 44 Thread t4 = new Thread(notifyTask, "NT1"); //will notify喚醒線程 45 46 //starting all waiting thread 47 t1.start(); 48 t2.start(); 49 t3.start(); 50 51 //pause to ensure all waiting thread started successfully 52 // 休眠200毫秒 53 Thread.sleep(200); 54 55 //starting notifying thread 56 t4.start(); 57 58 } 59 60 /* 61 * wait and notify can only be called from synchronized method or bock 62 * 63 * synchronized方法,需要獲取到同步鎖才可以執行里面的邏輯的 64 */ 65 private synchronized void shouldGo() throws InterruptedException { 66 // go默認是false 67 while (go != true) { 68 System.out.println(Thread.currentThread() 69 + " is going to wait on this object"); 70 // 無限期等待 71 wait(); //release lock and reacquires on wakeup 72 System.out.println(Thread.currentThread() + " is woken up"); 73 } 74 go = false; //resetting condition 75 } 76 77 /* 78 * both shouldGo() and go() are locked on current object referenced by "this" keyword 79 */ 80 private synchronized void go() { 81 while (go == false) { 82 System.out.println(Thread.currentThread() 83 + " is going to notify all or one thread waiting on this object"); 84 85 go = true; //making condition true for waiting thread 86 //notify(); // only one out of three waiting thread WT1, WT2,WT3 will woke up 87 notifyAll(); // all waiting thread WT1, WT2,WT3 will woke up 88 } 89 } 90 91 92 }
11、Java線程中 yield。
答:1)、當調用Thread.yield()方法的時候,會給線程調度器一個當前線程願意讓出CPU使用的暗示,但是線程調度器可能會忽略這個暗示。
2)、yield方法對鎖的行為不會有影響的,不會讓當前線程讓出鎖。
1 package com.thread; 2 3 public class YieldDemo { 4 5 public static void main(String[] args) { 6 // 創建一個線程,使用的是匿名內部類 7 Runnable yieldTask = new Runnable() { 8 9 @Override 10 public void run() { 11 for (int i = 1; i <= 10; i++) { 12 System.out.println(Thread.currentThread().getName() + i); 13 if (i == 5) { 14 Thread.yield(); 15 } 16 } 17 } 18 }; 19 20 // 當線程A執行到5的時候,會不會讓給線程B執行呢。但是最終的決定權還是在線程調度器手上的。 21 Thread t1 = new Thread(yieldTask, "A"); 22 Thread t2 = new Thread(yieldTask, "B"); 23 t1.start(); 24 t2.start(); 25 } 26 27 }
12、Java線程中如何中斷線程。
答:已經被拋棄的方法。
1)、通過調用stop()方法停止線程,可以通過一個線程停止另外一個線程,這種方法太過暴力,也不安全,比如線程A調用線程B的stop方法,去停止線程B,調用這個方法的時候,線程A其實並不知道線程B執行的具體情況,這種突然間的停止會導致線程B的一些清理工作無法完成,還有一個情況就是執行stop方法后,線程B會馬上釋放鎖,有可能會引發數據不同步的問題。
2)、通過調用suspend()方法和resume()方法。
目前使用的方法,如何中斷線程。
1)、調用Interrupt()方法,通知線程應該中斷了。含義是通知線程你應該中斷了,該線程到底是中斷還是繼續執行呢,應該由這個線程自己去處理。
a)、如果線程處於被阻塞狀態,例如sleep、wait、join狀態,那么線程將立即退出被阻塞狀態,並拋出一個InterruptedException異常。
b)、如果線程處於正常活動狀態,那么會將該線程的中斷標志設置為true。被設置中斷標志的線程將繼續正常運行,不受影響。
2)、Interrupt()方法並不能真正的中斷線程,需要被調用的線程配合中斷。
a)、在正常運行任務的時候,經常檢查本線程的中斷標志位,如果被設置了中斷標志就自行停止線程。在調用阻塞方法的時候,正確去處理InterruptedException異常,例如,在catch異常后就結束線程。
b)、如果線程處於正常活動狀態,那么會將該線程的中斷標志設置為true。被設置中斷標志的線程將繼續正常運行,不受影響。
13、Java線程中線程狀態以及狀態之間的轉換。
1)、新建:通過實現Runnable接口或者繼承Thread類可以得到一個線程類,通過new一個線程實例就進入了new即新建狀態了。
2)、可運行:此時調用t.start()方法,就進入到了可運行runnable狀態。若此時處於runnable狀態的線程被OS選中,並獲得了時間片之后j就會進入Running狀態,Running狀態僅僅是邏輯上的划分。
3)、運行中:如果running狀態的線程調用了yield()方法可能會讓出CPU回到runnable狀態,當然這取決於操作系統的調度,yield只是起到了一個建議的作用。如果時間片用完了,線程還沒有結束的話也會進入到runnable狀態。
4)、阻塞:如果處於Running狀態的線程又等待用戶輸入或者調用thrad.sleep()方法則會進入阻塞狀態。此時只會讓出CPU,如果當前線程已經獲得鎖的話,是不會對占有鎖有任何影響的,即不會釋放已經獲得的鎖。
5)、鎖池:此外,處於Running狀態、runnable狀態的線程執行synchronized方法或者方法塊的時候,發現並未獲取到相應的鎖,也會進入到阻塞的狀態,同時會被放入到鎖對象的鎖池當中。
6)、等待隊列:如果處於Running狀態運行中的線程,調用了wait方法之后呢,就會進入到限期或者非限期的等待狀態,同時會被放入到鎖對象的等待隊列當中。
7)、等待隊列 -> 鎖池 -> 可運行: 處於等待隊列中的線程如果wait時間到了或者被其它線程調用notify或者notifyall去喚醒的話,則會被放入到鎖池當中,之后,位於鎖池中的線程一旦獲得了鎖,則會再次進入可運行runnable狀態當中,被OS選中之后,就會進入到Running狀態運行狀態。
8)、死亡:最后處於Running狀態的線程,在方法執行完畢或者異常退出,該線程就會結束,進入死亡terminated狀態。
方便面試回答,還是使用一下經典的圖,下圖展示了Java線程的生命周期,Java線程具有五種基本狀態:
1)、新建狀態(New):當線程對象對創建后,即進入了新建狀態,如:Thread t = new Thread ();
2)、就緒狀態(Runnable):當調用線程對象的start()方法后,線程就進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經做好了准備,隨時等待CPU調度執行,並不是說執行了t.start()方法后,此線程立即就會執行;
3)、運行狀態(Running):當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;
4)、阻塞狀態(Blocked):處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才有機會再次被CPU調用以進入到運行狀態。根據阻塞產生的原因不同,阻塞狀態又可以分為三種:
a)、等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
b)、同步阻塞:線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態;
c)、其他阻塞:通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5)、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。