標籤:als swa lin 需要 情況 簡單 變數 重試 線上
1、volatile關鍵字的作用
一個非常重要的問題,是每個學習、應用多線程的Java程式員都必須掌握的。理解volatile關鍵字的作用的前提是要理解Java記憶體模型,這裡就不講Java記憶體模型了,可以參見第31點,volatile關鍵字的作用主要有兩個:
(1)多線程主要圍繞可見度和原子性兩個特性而展開,使用volatile關鍵字修飾的變數,保證了其在多線程之間的可見度,即每次讀取到volatile變數,一定是最新的資料
(2)代碼底層執行不像我們看到的進階語言----Java程式這麼簡單,它的執行是Java代碼-->位元組碼-->根據位元組碼執行對應的C/C++代碼-->C/C++代碼被編譯成組合語言-->和硬體電路互動,現實中,為了擷取更好的效能JVM可能會對指令進行重排序,多線程下可能會出現一些意想不到的問題。使用volatile則會對禁止語義重排序,當然這也一定程度上降低了代碼執行效率
從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger。
2、Java中如何擷取到線程dump檔案
死迴圈、死結、阻塞、頁面開啟慢等問題,打線程dump是最好的解決問題的途徑。所謂線程dump也就是線程堆棧,擷取到線程堆棧有兩步:
(1)擷取到線程的pid,可以通過使用jps命令,在Linux環境下還可以使用ps -ef | grep java
(2)列印線程堆棧,可以通過使用jstack pid命令,在Linux環境下還可以使用kill -3 pid
另外提一點,Thread類提供了一個getStackTrace()方法也可以用於擷取線程堆棧。這是一個執行個體方法,因此此方法是和具體線程執行個體綁定的,每次擷取擷取到的是具體某個線程當前啟動並執行堆棧
3、一個線程如果出現了運行時異常會怎麼樣
如果這個異常沒有被捕獲的話,這個線程就停止執行了。另外重要的一點是:如果這個線程持有某個某個對象的監視器,那麼這個對象監視器會被立即釋放
4、如何在兩個線程之間共用資料
通過線上程之間共用對象就可以了,然後通過wait/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是為線程之間共用資料而設計的
5、sleep方法和wait方法有什麼區別
這個問題常問,sleep方法和wait方法都可以用來放棄CPU一定的時間,不同點在於如果線程持有某個對象的監視器,sleep方法不會放棄這個對象的監視器,wait方法會放棄這個對象的監視器
6、生產者消費者模型的作用是什麼
這個問題很理論,但是很重要:
(1)通過平衡生產者的生產能力和消費者的消費能力來提升整個系統的運行效率,這是生產者消費者模型最重要的作用
(2)解耦,這是生產者消費者模型附帶的作用,解耦意味著生產者和消費者之間的聯絡少,聯絡越少越可以獨自發展而不需要收到相互的制約
6、wait()方法和notify()/notifyAll()方法在放棄對象監視器時有什麼區別
wait()方法和notify()/notifyAll()方法在放棄對象監視器的時候的區別在於:wait()方法立即釋放對象監視器,notify()/notifyAll()方法則會等待線程剩餘代碼執行完畢才會放棄對象監視器。
7、Linux環境下如何尋找哪個線程使用CPU最長
這是一個比較偏實踐的問題,這種問題我覺得挺有意義的。可以這麼做:
(1)擷取項目的pid,jps或者ps -ef | grep java,這個前面有講過
(2)top -H -p pid,順序不能改變
8、Thread.sleep(0)的作用是什麼
由於Java採用搶佔式的線程調度演算法,因此可能會出現某條線程常常擷取到CPU控制權的情況,為了讓某些優先順序比較低的線程也能擷取到CPU控制權,可以使用Thread.sleep(0)手動觸發一次作業系統分配時間片的操作,這也是平衡CPU控制權的一種操作。
9、什麼是自旋
很多synchronized裡面的代碼只是一些很簡單的代碼,執行時間非常快,此時等待的線程都加鎖可能是一種不太值得的操作,因為線程阻塞涉及到使用者態和核心態切換的問題。既然synchronized裡面的代碼執行得非常快,不妨讓等待鎖的線程不要被阻塞,而是在synchronized的邊界做忙迴圈,這就是自旋。如果做了多次忙迴圈發現還沒有獲得鎖,再阻塞,這樣可能是一種更好的策略。
10、什麼是CAS
CAS,全稱為Compare and Swap,即比較-替換。假設有三個運算元:記憶體值V、舊的預期值A、要修改的值B,若且唯若預期值A和記憶體值V相同時,才會將記憶體值修改為B並返回true,否則什麼都不做並返回false。當然CAS一定要volatile變數配合,這樣才能保證每次拿到的變數是主記憶體中最新的那個值,否則舊的預期值A對某條線程來說,永遠是一個不會變的值A,只要某次CAS操作失敗,永遠都不可能成功。
11、什麼是樂觀鎖和悲觀鎖
(1)樂觀鎖:就像它的名字一樣,對於並發間操作產生的安全執行緒問題持樂觀狀態,樂觀鎖認為競爭不總是會發生,因此它不需要持有鎖,將比較-替換這兩個動作作為一個原子操作嘗試去修改記憶體中的變數,如果失敗則表示發生衝突,那麼就應該有相應的重試邏輯。
(2)悲觀鎖:還是像它的名字一樣,對於並發間操作產生的安全執行緒問題持悲觀狀態,悲觀鎖認為競爭總是會發生,因此每次對某資源進行操作時,都會持有一個獨佔的鎖,就像synchronized,不管三七二十一,直接上了鎖就操作資源了。
Java 多線程的一些問題