多个线程访问统一对象的不同方法_不会多线程还想进BAT?精选19道多线程面试题,有答案边看边学...
一. Java程序如何停止一個線程?
建議使用”異常法”來終止線程的繼續運行。在想要被中斷執行的線程中, 調用 interrupted()方法,該方法用來檢驗當前線程是否已經被中斷,即該線程 是否被打上了中斷的標記,并不會使得線程立即停止運行,如果返回 true,則 拋出異常,停止線程的運行。在線程外,調用 interrupt()方法,使得該線程打 上中斷的標記。
二. 說一下 java 中的多線程。
1. Java 中實現多線程的四種方式(創建多線程的四種方式)?
①. 繼承 Thread 類創建線程類
- 定義 Thread 類的子類,并重寫該類的 run 方法,該 run 方法的方 法體就代表了線程要完成的任務。因此把 run()方法稱為執行體。
- 創建 Thread 子類的實例,即創建了線程對象。
- 調用線程對象的 start()方法來啟動該線程。
②. 通過 Runnable 接口創建線程類
- 定義 Runnable 接口的實現類,并重寫該接口的 run()方法,該 run() 方法的方法體同樣是該線程的線程執行體。
- 創建 Runnable 實現類的實例,并依此實例作為 Thread 的 target 來創建 Thread 對象,該 Thread 對象才是真正的線程對象。
- 調用線程對象的 start()方法來啟動該線程。
③. 通過 Callable 和 Future 創建線程
- 創建 Callable 接口的實現類,并實現 call()方法,該 call()方法將作 為線程執行體,并且有返回值。
- 創建 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call()方法的返回值。
- 使用 FutureTask 對象作為 Thread 對象的 target 創建并啟動新線 程。
- 調用 FutureTask 對象的 get()方法來獲得子線程執行結束后的返回值。
④. 通過線程池創建線程
利用線程池不用 new 就可以創建線程,線程可復用,利用 Executors 創 建線程池。
擴展1:Java 中 Runnable 和 Callable 有什么不同?
- Callable 定義的方法是 call(),而 Runnable 定義的方法是 run()。
- Callable 的 call 方法可以有返回值,而 Runnable 的 run 方法不能有 返回值。
- Callable 的 call 方法可拋出異常,而 Runnable 的 run 方法不能拋出 異常。
擴展2:一個類是否可以同時繼承 Thread 和實現 Runnable接口?<
可以。比如下面的程序可以通過編譯。因為 Test 類從 Thread 類中繼承了 run()方法,這個 run()方法可以被當作對 Runnable 接口的實現。
public class Test extends Thread implements Runnable { public static void main(String[] args) { Thread t = new Thread(new Test()); t.start(); }}2. 實現多線程的同步。
在多線程的環境中,經常會遇到數據的共享問題,即當多個線程需要訪問同 一資源時,他們需要以某種順序來確保該資源在某一時刻只能被一個線程使用, 否則,程序的運行結果將會是不可預料的,在這種情況下,就必須對數據進行 同步。
在 Java 中,提供了四種方式來實現同步互斥訪問: synchronized 和 Lock 和 wait()/notify()/notifyAll()方法和 CAS。
①. synchronized 的用法。
A . 同步代碼塊
synchronized 塊寫法: synchronized(object) {}表示線程在執行的時候會將 object 對象上鎖。(注意這個對象可以是任意 類的對象,也可以使用 this 關鍵字或者是 class 對象)。
可能一個方法中只有幾行代碼會涉及到線程同步問題,所以 synchronized 塊 比 synchronized 方法更加細粒度地控制了多個線程的訪問, 只有 synchronized 塊中的內容不能同時被多個線程所訪問,方法中的其他語句仍然 可以同時被多個線程所訪問(包括 synchronized 塊之前的和之后的)。
B . 修飾非靜態的方法
當 synchronized 關鍵字修飾一個方法的時候,該方法叫做同步方法。
Java 中的每個對象都有一個鎖(lock),或者叫做監視器(monitor), 當一個線程訪問某個對象的 synchronized 方法時,將該對象上鎖,其他任何 線程都無法再去訪問該對象的 synchronized 方法了(這里是指所有的同步方 法,而不僅僅是同一個方法),直到之前的那個線程執行方法完畢后(或者是 拋出了異常),才將該對象的鎖釋放掉,其他線程才有可能再去訪問該對象的 synchronized 方法。
注意這時候是給對象上鎖,如果是不同的對象,則各個對象之間沒有限制 關系。
注意,如果一個對象有多個 synchronized 方法,某一時刻某個線程已經進入 到了某個 synchronized 方法,那么在該方法沒有執行完畢前,其他線程是無法訪 問該對象的任何 synchronized 方法的。
C . 修飾靜態的方法
當一個 synchronized 關鍵字修飾的方法同時又被 static 修飾,之前說過, 非靜態的同步方法會將對象上鎖,但是靜態方法不屬于對象,而是屬于類,它 會將這個方法所在的類的 Class 對象上鎖。一個類不管生成多少個對象,它們 所對應的是同一個 Class 對象。
因此,當線程分別訪問同一個類的兩個對象的兩個 static,synchronized 方法時,它們的執行順序也是順序的,也就是說一個線程先去執行方法,執行 完畢后另一個線程才開始。
結論:
- synchronized 方法是一種粗粒度的并發控制,某一時刻,只能有一個線 程執行該 synchronized 方法。
- synchronized 塊則是一種細粒度的并發控制,只會將塊中的代碼同步, 位于方法內,synchronized 塊之外的其他代碼是可以被多個線程同時訪問到 的。
②.Lock 的用法。
使用 Lock 必須在 try-catch-finally 塊中進行,并且將釋放鎖的操作放在 finally 塊中進行,以保證鎖一定被釋放,防止死鎖的發生。通常使用 Lock 來 進行同步的話,是以下面這種形式去使用的:
Lock lock = ...;lock.lock();try{ //處理任務}catch(Exception ex){}finally{ lock.unlock(); //釋放鎖}Lock 和 synchronized 的區別和 Lock 的優勢。你需要實現 一個高效的緩存,它允許多個用戶讀,但只允許一個用戶寫,以此 來保持它的完整性,你會怎樣去實現它?
- Lock 是一個接口,而 synchronized 是 Java 中的關鍵字, synchronized 是內置的語言實現;
- synchronized 在發生異常時,會自動釋放線程占有的鎖,因此不會導 致死鎖現象發生;而 Lock 在發生異常時,如果沒有主動通過 unLock()去釋放 鎖,則很可能造成死鎖現象,因此使用 Lock 時需要在 finally 塊中釋放鎖;
- Lock 可以讓等待鎖的線程響應中斷(可中斷鎖),而 synchronized 卻不行,使用 synchronized 時,等待的線程會一直等待下去,不能夠響應中 斷(不可中斷鎖);
- 通過 Lock 可以知道有沒有成功獲取鎖(tryLock()方法:如果獲取 了鎖,則返回 true;否則返回 false,也就說這個方法無論如何都會立即返回。 在拿不到鎖時不會一直在那等待。),而 synchronized 卻無法辦到。
- Lock 可以提高多個線程進行讀操作的效率(讀寫鎖)。
- Lock 可以實現公平鎖,synchronized 不保證公平性。 在性能上來說,如果線程競爭資源不激烈時,兩者的性能是差不多的,而 當競爭資源非常激烈時(即有大量線程同時競爭),此時 Lock 的性能要遠遠優 于 synchronized。所以說,在具體使用時要根據適當情況選擇。
擴展1: volatile 和 synchronized 區別。
- volatile 是變量修飾符,而 synchronized 則作用于代碼塊或方法。
- volatile 不會對變量加鎖,不會造成線程的阻塞;synchronized 會 對變量加鎖,可能會造成線程的阻塞。
- volatile 僅能實現變量的修改可見性,并不能保證原子性;而 synchronized 則 可 以 保 證 變 量 的 修 改 可 見 性 和 原 子 性 。 (synchronized 有兩個重要含義:它確保了一次只有一個線程可以執 行代碼的受保護部分(互斥),而且它確保了一個線程更改的數據對于 其它線程是可見的(更改的可見性),在釋放鎖之前會將對變量的修改 刷新到主存中)。
- volatile 標記的變量不會被編譯器優化,禁止指令重排序; synchronized 標記的變量可以被編譯器優化。
擴展 2:什么場景下可以使用 volatile 替換 synchronized?
只需要保證共享資源的可見性的時候可以使用 volatile 替代, synchronized 保證可操作的原子性,一致性和可見性。
③.wait()otify()otifyAll()的用法(Java 中怎樣喚醒一個阻塞的線程?)。
在 Java 發展史上曾經使用 suspend()、resume()方法對于線程進行阻塞喚醒,但隨之出 現很多問題,比較典型的還是死鎖問題。
解決方案可以使用以對象為目標的阻塞,即利用 Object 類的 wait()和 notify()方法實現 線程阻塞。
首先,wait、notify 方法是針對對象的,調用任意對象的 wait()方法都將導致線程阻塞, 阻塞的同時也將釋放該對象的鎖,相應地,調用任意對象的 notify()方法則將隨機解除該對 象阻塞的線程,但它需要重新獲取改對象的鎖,直到獲取成功才能往下執行;其次,wait、 notify 方法必須在 synchronized 塊或方法中被調用,并且要保證同步塊或方法的鎖對象與調 用 wait、notify 方法的對象是同一個,如此一來在調用 wait 之前當前線程就已經成功獲取 某對象的鎖,執行 wait 阻塞后當前線程就將之前獲取的對象鎖釋放。
擴展 1: 為什么 wait(),notify(),notifyAll()等方法都定義在 Object 類中?
因為這三個方法都需要定義在同步代碼塊或同步方法中,這些方法的調用是依賴鎖對 象的,而同步代碼塊或同步方法中的鎖對象可以是任意對象,那么能被任意對象調用的方 法一定定義在 Object 類中。
擴展 2: notify()和 notifyAll()有什么區別?
notify()和 notifyAll()都是 Object 對象用于通知處在等待該對象的線程的方法。
void notify(): 喚醒一個正在等待該對象的線程,進入就緒隊列等待 CPU 的調度。
void notifyAll(): 喚醒所有正在等待該對象的線程,進入就緒隊列等待 CPU 的調度。
兩者的最大區別在于:
notifyAll 使所有原來在該對象上等待被 notify 的線程統統退出 wait 的狀態,變成等待該對 象上的鎖,一旦該對象被解鎖,他們就會去競爭。 notify 他只是選擇一個 wait 狀態線程進行通知,并使它獲得該對象上的鎖,但不驚動其他 同樣在等待被該對象 notify 的線程們,當第一個線程運行完畢以后釋放對象上的鎖,此時 如果該對象沒有再次使用 notify 語句,即便該對象已經空閑,其他 wait 狀態等待的線程由 于沒有得到該對象的通知,繼續處在 wait 狀態,直到這個對象發出一個 notify 或 notifyAll, 它們等待的是被 notify 或 notifyAll,而不是鎖。
④.CAS
它是一種非阻塞的同步方式。具體參見上面的部分。
擴展一:同步鎖的分類?
- Synchronized 和 Lock 都是悲觀鎖。
- 樂觀鎖,CAS 同步原語,如原子類,非阻塞同步方式。
擴展二:鎖的分類?
- 一種是代碼層次上的,如 java 中的同步鎖,可重入鎖,公平鎖,讀寫鎖。另外一種是數據庫層次上的,比較典型的就是悲觀鎖和樂觀鎖,表鎖,行鎖,頁鎖。
擴展三:java 中的悲觀鎖和樂觀鎖?
- 悲觀鎖:悲觀鎖是認為肯定有其他線程來爭奪資源,因此不管到底會不會發生爭奪, 悲觀鎖總是會先去鎖住資源,會導致其它所有需要鎖的線程掛起,等待持有鎖的線程釋放 鎖。Synchronized 和 Lock 都是悲觀鎖。
- 樂觀鎖:每次不加鎖,假設沒有沖突去完成某項操作,如果因為沖突失敗就重試,直 到成功為止。就是當去做某個修改或其他操作的時候它認為不會有其他線程來做同樣的操 作(競爭),這是一種樂觀的態度,通常是基于 CAS 原子指令來實現的。CAS 通常不會將 線程掛起,因此有時性能會好一些。樂觀鎖的一種實現方式——CAS。
三. 實現線程之間的通信?
當線程間是可以共享資源時,線程間通信是協調它們的重要的手段。
1. Object 類中 wait()otify()otifyAll()方法。
2. 用 Condition 接口。
- Condition 是被綁定到 Lock 上的,要創建一個 Lock 的 Condition 對 象必須用 newCondition()方法。在一個 Lock 對象里面可以創建多個 Condition 對象,線程可以注冊在指定的 Condition 對象中,從而可以有 選擇性地進行線程通知,在線程調度上更加靈活。
- 在 Condition 中,用 await()替換 wait(),用 signal()替換 notify(), 用 signalAll()替換 notifyAll(),傳統線程的通信方式, Condition 都可以實現。 調用 Condition 對象中的方法時,需要被包含在 lock()和 unlock()之間。
3. 管道實現線程間的通信。
- 實現方式:一個線程發送數據到輸出管道流,另一個線程從輸入管道流中 讀取數據。
- 基本流程:
- 1)創建管道輸出流 PipedOutputStream pos 和管道輸入流 PipedInputStream pis。
- 2)將 pos 和 pis 匹配,pos.connect(pis)。
- 3)將 pos 賦給信息輸入信息的線程,pis 賦給獲取信息的線程,就可以實 現線程間的通訊了。
- 缺點:
- 1)管道流只能在兩個線程之間傳遞數據。
- 線程 consumer1 和 consumer2 同時從 pis 中 read 數據,當線程 producer 往管道流中寫入一段數據(1,2,3,4,5,6)后,每一個時刻只有一個 線程能獲取到數據,并不是兩個線程都能獲取到 producer 發送來的數據,因 此一個管道流只能用于兩個線程間的通訊。
- 2)管道流只能實現單向發送,如果要兩個線程之間互通訊,則需要兩個管道流。
- 線程 producer 通過管道流向線程 consumer 發送數據,如果線程 consumer 想給線程 producer 發送數據,則需要新建另一個管道流 pos1 和 pis1,將 pos1 賦給 consumer1,將 pis1 賦給 producer1。
4. 使用 volatile 關鍵字
見上面部分。
四. 如何確保線程安全?
如果多個線程同時運行某段代碼,如果每次運行結果和單線程運行的結果 是一樣的,而且其他變量的值也和預期的是一樣的,就是線程安全的。
Synchronized,Lock,原子類(如 atomicinteger 等),同步容器、并 發容器、 阻塞隊列 、 同步輔助類(比 如 CountDownLatch, Semaphore, CyclicBarrier)。
五. 多線程的優點和缺點?
1. 優點:
- 充分利用 cpu,避免 cpu 空轉。
- 程序響應更快。
2. 缺點:
- 上下文切換的開銷
- 當 CPU 從執行一個線程切換到執行另外一個線程的時候,它需要先存儲當 前線程的本地的數據,程序指針等,然后載入另一個線程的本地數據,程序指 針等,最后才開始執行。這種切換稱為“上下文切換”。CPU 會在一個上下文 中執行一個線程,然后切換到另外一個上下文中執行另外一個線程。上下文切 換并不廉價。如果沒有必要,應該減少上下文切換的發生。
- 增加資源消耗
- 線程在運行的時候需要從計算機里面得到一些資源。 除了 CPU,線程還需 要一些內存來維持它本地的堆棧。它也需要占用操作系統中一些資源來管理線 程。
- 編程更復雜
- 在多線程訪問共享數據的時候,要考慮線程安全問題。
六. 寫出 3 條你遵循的多線程最佳實踐。
七. 多線程的性能一定就優于單線程嗎?
不一定,要看具體的任務以及計算機的配置。比如說:
- 對于單核 CPU,如果是 CPU 密集型任務,如解壓文件,多線程的性能反 而不如單線程性能,因為解壓文件需要一直占用 CPU 資源,如果采用多線程, 線程切換導致的開銷反而會讓性能下降。如果是交互類型的任務,肯定是需要 使用多線程的。
- 對于多核 CPU,對于解壓文件來說,多線程肯定優于單線程,因為多個線 程能夠更加充分利用每個核的資源。
八. 多線程中鎖的種類。
1. 可重入鎖
ReentrantLock 和 synchronized 都是可重入鎖。
如果當前線程已經獲得了某個監視器對象所持有的鎖,那么該線程在該方法 中調用另外一個同步方法也同樣持有該鎖。
比如:
public sychrnozied void test() { xxxxxx; test2();}public sychronized void test2() { yyyyy;}在上面代碼段中,執行 test 方法需要獲得當前對象作為監視器的對象鎖, 但方法中又調用了 test2 的同步方法。
如果鎖是具有可重入性的話,那么該線程在調用 test2 時并不需要再次獲 得當前對象的鎖,可以直接進入 test2 方法進行操作。
可重入鎖最大的作用是避免死鎖。如果鎖是不具有可重入性的話,那么該 線程在調用 test2 前會等待當前對象鎖的釋放,實際上該對象鎖已被當前線程 所持有,不可能再次獲得,那么線程在調用同步方法、含有鎖的方法時就會產 生死鎖。
2. 可中斷鎖
顧名思義,就是可以響應中斷的鎖。
在 Java 中,synchronized 不是可中斷鎖,而 Lock 是可中斷鎖。 lockInterruptibly()的用法已經體現了 Lock 的可中斷性。如果某一線程 A 正 在執行鎖中的代碼,另一線程 B 正在等待獲取該鎖,可能由于等待時間過長, 線程 B 不想等待了,想先處理其他事情,我們可以讓它中斷自己或者在別的線 程中斷它,這種就是可中斷鎖。
3. 公平鎖
在 Java 中,synchronized 就是非公平鎖,它無法保證等待的線程獲取鎖 的順序。而對于 ReentrantLock 和 ReentrantReadWriteLock,它默認情況 下是非公平鎖,但是可以設置為公平鎖。
公平鎖即盡量以請求鎖的順序來獲取鎖。比如同是有多個線程在等待一個 鎖,當這個鎖被釋放時,等待時間最久的線程(最先請求的線程)會獲得該鎖, 這種就是公平鎖。
4. 讀寫鎖
正因為有了讀寫鎖,才使得多個線程之間的讀操作不會發生沖突。 ReadWriteLock 就是讀寫鎖,它是一個接口,ReentrantReadWriteLock 實 現了這個接口。可以通過 readLock()獲取讀鎖,通過 writeLock()獲取寫鎖。
九. 鎖優化
1. 自旋鎖
- 為了讓線程等待,讓線程執行一個忙循環(自旋)。需要物理機器有一個以 上的處理器。自旋等待雖然避免了線程切換的開銷,但它是要占用處理器時間 的,所以如果鎖被占用的時間很短,自旋等待的效果就會非常好,反之自旋的 線程只會白白消耗處理器資源。自旋次數的默認值是 10 次,可以使用參數 -XX:PreBlockSpin 來更改。
- 自適應自旋鎖:自旋的時間不再固定,而是由前一次在同一個鎖上的自旋 時間及鎖的擁有者的狀態來決定。
2. 鎖清除
- 指虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不 可能存在共享數據競爭的鎖進行清除(逃逸分析技術:在堆上的所有數據都不會 逃逸出去被其它線程訪問到,可以把它們當成棧上數據對待)。
3. 鎖粗化
如果虛擬機探測到有一串零碎的操作都對同一個對象加鎖,將會把加鎖同 步的范圍擴展到整個操作序列的外部。
4. 輕量級鎖
- 在代碼進入同步塊時,如果此同步對象沒有被鎖定,虛擬機首先將在當前 線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲所對象目 前的 Mark Word 的拷貝。然后虛擬機將使用 CAS 操作嘗試將對象的 Mark Word 更新為執行 Lock Record 的指針。如果成功,那么這個線程就擁有了該 對象的鎖。如果更新操作失敗,虛擬機首先會檢查對象的 Mark Word 是否指 向當前線程的棧幀,如果是就說明當前線程已經擁有了這個對象的鎖,否則說 明這個對象已經被其它線程搶占。如果有兩條以上的線程爭用同一個鎖,那輕 量級鎖就不再有效,要膨脹為重量級鎖。
- 解鎖過程:如果對象的 Mark Word 仍然指向著線程的鎖記錄,那就用 CAS 操作把對象當前的 Mark Word 和和線程中復制的 Displaced Mark Word 替 換回來,如果替換成功,整個過程就完成。如果失敗,說明有其他線程嘗試過 獲取該鎖,那就要在釋放鎖的同時,喚醒被掛起的線程。
- 輕量級鎖的依據:對于絕大部分的鎖,在整個同步周期內都是不存在競爭 的。
- 傳統鎖(重量級鎖)使用操作系統互斥量來實現的。
HotSpot 虛擬機的對象的內存布局:對象頭(Object Header)分為兩部分信息嗎,第 一部分(Mark Word)用于存儲對象自身的運行時數據,另一個部分用于存儲指向方法區 對象數據類型的指針,如果是數組的話,還會由一個額外的部分用于存儲數組的長度。
32 位 HotSpot 虛擬機中對象未被鎖定的狀態下, Mark Word 的 32 個 Bits 空間中 25 位 用于存儲對象哈希碼,4 位存儲對象分代年齡,2 位存儲鎖標志位,1 位固定為 0。
HotSpot 虛擬機對象頭 Mark Word
存儲內容標志位狀態對象哈希碼、對象分代年齡01未鎖定指向鎖記錄的指針00輕量級鎖定指向重量級鎖的指針10膨脹(重量級鎖)空,不記錄信息11GC 標記偏向線程 ID,偏向時間戳、對象分代年齡01可偏向
5. 偏向鎖
- 目的是消除在無競爭情況下的同步原語,進一步提高程序的運行性能。鎖 會偏向第一個獲得它的線程,如果在接下來的執行過程中,該鎖沒有被其它線 程獲取,則持有鎖的線程將永遠不需要再進行同步。
- 當鎖第一次被線程獲取的時候,虛擬機將會把對象頭中的標志位設為 01, 同時使用 CAS 操作把獲取到這個鎖的線程的 ID 記錄在對象的 Mark Word 之 中,如果成功,持有偏向鎖的線程以后每次進入這個鎖相關的同步塊時,都可 以不進行任何同步操作。
- 當有另一個線程去嘗試獲取這個鎖時,偏向模式就宣告結束。根據鎖對象 目前是否處于被鎖定的狀態,撤銷偏向后恢復到未鎖定或輕量級鎖定狀態。
十. wait()和 sleep()的區別。
特別注意: sleep 和 wait 必須捕獲異常(Thread.sleep()和 Object.wait() 都會拋出 InterruptedException), notify 和 notifyAll 不需要捕獲異常。
十一. Java 中 interrupted() 和 isInterrupted()方法的區別?
二個方法都是判斷線程是否停止的方法。
十二. Java 創建線程之后,直接調用 start()方法和 run()的區別 ?
十三. 什么是線程的上下文切換?
對于單核 CPU,CPU 在一個時刻只能運行一個線程,當在運行一個線程的 過程中轉去運行另外一個線程,這個叫做線程上下文切換(對于進程也是類似)。
線程上下文切換過程中會記錄程序計數器、CPU 寄存器的狀態等數據。
雖然多線程可以使得任務執行的效率得到提升,但是由于在線程切換時同 樣會帶來一定的開銷代價,并且多個線程會導致系統資源占用的增加,所以在 進行多線程編程時要注意這些因素。
十四. 怎么檢測一個線程是否擁有鎖?
在 java.lang.Thread 中有一個方法叫 holdsLock(Object obj),它返回 true,如果當且僅當當前線程擁有某個具體對象的鎖。
十五. 用戶線程和守護線程有什么區別?
當我們在 Java 程序中創建一個線程,它就被稱為用戶線程。將一個用戶線 程設置為守護線程的方法就是在調用start()方法之前, 調用對象的 setDamon(true)方法。一個守護線程是在后臺執行并且不會阻止 JVM 終止的 線程,守護線程的作用是為其他線程的運行提供便利服務。當沒有用戶線程在 運行的時候, JVM 關閉程序并且退出。一個守護線程創建的子線程依然是守護 線程。
守護線程的一個典型例子就是垃圾回收器。
十六. 什么是線程調度器?
線程調度器是一個操作系統服務,它負責為 Runnable 狀態的線程分配 CPU 時間。一旦我們創建一個線程并啟動它,它的執行便依賴于線程調度器的 實現。
十七. 線程的狀態。
版本 1.
在 Java 當中,線程通常都有五種狀態,創建、就緒、運行、阻塞和死亡。
- 第一是創建狀態。在生成線程對象,并沒有調用該對象的 start 方法,這是 線程處于創建狀態。
- 第二是就緒狀態。當調用了線程對象的 start 方法之后,該線程就進入了就 緒狀態,但是此時線程調度程序還沒有把該線程設置為當前線程,此時處于就 緒狀態。在線程運行之后,從等待或者睡眠中回來之后,也會處于就緒狀態。
- 第三是運行狀態。線程調度程序將處于就緒狀態的線程設置為當前線程, 此時線程就進入了運行狀態,開始運行 run 函數當中的代碼。
- 第四是阻塞狀態。線程正在運行的時候,被暫停,通常是為了等待某個事 件的發生(比如說某項資源就緒)之后再繼續運行。sleep,wait 等方法都可以導 致線程阻塞。
- 第五是死亡狀態。如果一個線程的 run 方法執行結束或者異常中斷后,該 線程就會死亡。對于已經死亡的線程,無法再使用 start 方法令其進入就緒。
版本 2.
一般來說,線程包括以下這幾個狀態:創建(new)、就緒(runnable)、運 行(running)、阻塞(blocked)、timed_waiting、waiting、消亡(dead)。
十八. 有三個線程 T1,T2,T3,怎么確保它們按順序執行?
join()方法。
十九. 在一個主線程中,要求有大量子線程執行完之后,主線程才執行完成。多種方式,考慮效率。
1. 在主函數中使用 join()方法。
t1.start();t2.start();t3.start();t1.join(); //不會導致 t1 和 t2 和 t3 的順序執行 t2.join();t3.join();System.out.println("Main finished");2. CountDownLatch,一個同步輔助類,在完成一組正在其他線程中執行 的操作之前,它允許一個或多個線程一直等待。
public class WithLatch { public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(3); for (int i = 0; i < 3; i++) { new ChildThead(i, latch).start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Main finished"); } static class ChildThead extends Thread { private int id = -1; private CountDownLatch latch = null; public ChildThead(int id, CountDownLatch latch) { this.id = id; this.latch = latch; } public void run() { try { Thread.sleep(Math.abs(new Random().nextint(5000))); System.out.println(String.format("Child Thread %dfinished總結
以上是生活随笔為你收集整理的多个线程访问统一对象的不同方法_不会多线程还想进BAT?精选19道多线程面试题,有答案边看边学...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 855和845差距
- 下一篇: 小米2s回退出厂版本_雷军很高兴,小米出