面試題(三):高併發程式設計
高併發程式設計
在 Java 5.0 提供了 java.util.concurrent(簡稱 JUC )包,在此包中增加了在併發程式設計中很常用的實用工具類,用於定義類似於執行緒的自定義子系統,包括執行緒池、非同步 IO 和輕量級任務框架。
01. 多執行緒和單執行緒的區別和聯絡:
答:
1.
在單核 CPU 中,將 CPU 分為很小的時間片,在每一時刻只能有一個執行緒在執行,是一種微觀上輪流佔用 CPU 的機制。
2.
多執行緒會存線上程上下文切換,會導致程式執行速度變慢,即採用一個擁有兩個執行緒的程序執行所需要的時間比一個執行緒的程序執行兩次所需要的時間要多一些。
3.
結論:即採用多執行緒不會提高程式的執行速度,反而會降低速度,但是對於使用者來說,可以減少使用者的響應時間。
02. 如何指定多個執行緒的執行順序?
解析:面試官會給你舉個例子,如何讓 10 個執行緒按照順序列印 0123456789?(寫程式碼實現)
答:
1.
設定一個 orderNum,每個執行緒執行結束之後,更新 orderNum,指明下一個要執行的執行緒。並且喚醒所有的等待執行緒。
2.
在每一個執行緒的開始,要 while 判斷 orderNum 是否等於自己的要求值!!不是,則 wait,是則執行本執行緒。
3.
03. 執行緒和程序的區別(必考)
答:
1.
程序是一個 “執行中的程式”,是系統進行資源分配和排程的一個獨立單位;
2.
執行緒是程序的一個實體,一個程序中擁有多個執行緒,執行緒之間共享地址空間和其它資源(所以通訊和同步等操作執行緒比程序更加容易);
3.
執行緒上下文的切換比程序上下文切換要快很多。
4.
·
(1)程序切換時,涉及到當前程序的 CPU 環境的儲存和新被排程執行程序的 CPU 環境的設定。
·
(2)執行緒切換僅需要儲存和設定少量的暫存器內容,不涉及儲存管理方面的操作。
·
04. 多執行緒產生死鎖的 4 個必要條件?
答:
1.
互斥條件:一個資源每次只能被一個執行緒使用;
2.
請求與保持條件:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放;
3.
不剝奪條件:程序已經獲得的資源,在未使用完之前,不能強行剝奪;
4.
迴圈等待條件:若干執行緒之間形成一種頭尾相接的迴圈等待資源關係。
5.
面試官:如何避免死鎖?(經常接著問這個問題哦
答:指定獲取鎖的順序,舉例如下:
1.
比如某個執行緒只有獲得 A 鎖和 B 鎖才能對某資源進行操作,在多執行緒條件下,如何避免死鎖?
2.
獲得鎖的順序是一定的,比如規定,只有獲得 A 鎖的執行緒才有資格獲取 B 鎖,按順序獲取鎖就可以避免死鎖!!!
3.
05. sleep( ) 和 wait( n)、wait( ) 的區別:
答:
1.
sleep 方法:是 Thread 類的靜態方法,當前執行緒將睡眠 n 毫秒,執行緒進入阻塞狀態。當睡眠時間到了,會解除阻塞,進行可執行狀態,等待 CPU 的到來。睡眠不釋放鎖(如果有的話);
2.
wait 方法:是 Object 的方法,必須與 synchronized 關鍵字一起使用,執行緒進入阻塞狀態,當 notify 或者 notifyall 被呼叫後,會解除阻塞。但是,只有重新佔用互斥鎖之後才會進入可執行狀態。睡眠時,釋放互斥鎖。
3.
06. synchronized 關鍵字:
答:
底層實現:
1.
進入時,執行 monitorenter,將計數器 +1,釋放鎖 monitorexit 時,計數器-1;
2.
當一個執行緒判斷到計數器為 0 時,則當前鎖空閒,可以佔用;反之,當前執行緒進入等待狀態。
3.
含義:(monitor 機制)
Synchronized 是在加鎖,加物件鎖。物件鎖是一種重量鎖(monitor),synchronized 的鎖機制會根據執行緒競爭情況在執行時會有偏向鎖(單一執行緒)、輕量鎖(多個執行緒訪問 synchronized 區域)、物件鎖(重量鎖,多個執行緒存在競爭的情況)、自旋鎖等。
該關鍵字是一個幾種鎖的封裝。
07. volatile 關鍵字
解析:關於指令重排序的問題,可以查閱 DCL 雙檢鎖失效相關資料。
答:
該關鍵字可以保證可見性不保證原子性。
功能:
1.
主記憶體和工作記憶體,直接與主記憶體產生互動,進行讀寫操作,保證可見性;
2.
禁止 JVM 進行的指令重排序。
3.
08. ThreadLocal(執行緒區域性變數)關鍵字:
答:
當使用 ThreadLocal 維護變數時,其為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立的改變自己的副本,而不會影響其他執行緒對應的副本。
ThreadLocal 內部實現機制:
1.
每個執行緒內部都會維護一個類似 HashMap 的物件,稱為 ThreadLocalMap,裡邊會包含若干了 Entry(K-V 鍵值對),相應的執行緒被稱為這些 Entry 的屬主執行緒;
2.
Entry 的 Key 是一個 ThreadLocal 例項,Value 是一個執行緒特有物件。Entry 的作用即是:為其屬主執行緒建立起一個 ThreadLocal 例項與一個執行緒特有物件之間的對應關係;
3.
Entry 對 Key 的引用是弱引用;Entry 對 Value 的引用是強引用。
4.
09. Atomic 關鍵字:
答:可以使基本資料型別以原子的方式實現自增自減等操作。
10. 執行緒池有了解嗎?(必考)
答:
java.util.concurrent.ThreadPoolExecutor 類就是一個執行緒池。客戶端呼叫 ThreadPoolExecutor.submit(Runnable task) 提交任務,執行緒池內部維護的工作者執行緒的數量就是該執行緒池的執行緒池大小,有 3 種形態:
·
當前執行緒池大小 :表示執行緒池中實際工作者執行緒的數量;
·
最大執行緒池大小 (maxinumPoolSize):表示執行緒池中允許存在的工作者執行緒的數量上限;
·
核心執行緒大小 (corePoolSize ):表示一個不大於最大執行緒池大小的工作者執行緒數量上限。
1.
如果執行的執行緒少於 corePoolSize,則 Executor 始終首選新增新的執行緒,而不進行排隊;
2.
如果執行的執行緒等於或者多於 corePoolSize,則 Executor 始終首選將請求加入佇列,而不是新增新執行緒;
3.
如果無法將請求加入佇列,即佇列已經滿了,則建立新的執行緒,除非建立此執行緒超出 maxinumPoolSize, 在這種情況下,任務將被拒絕。
4.
限於篇幅有限,更多高併發程式設計中的問題,請參考:
小結:本小節內容涉及到 Java 中多執行緒程式設計,執行緒安全等知識,是面試中的重點和難點。