Java 併發中考察頻率較高的有線程、線程池、鎖、線程間的等待和喚醒、線程特性和阻塞隊列等。
線程
1. 線程的狀態有哪些?
參考回答:
- new:新創建的線程
- Ready:準備就緒的線程,由於CPU分配的時間片的關係,此時的任務不在執行過程中。
- Running:正在執行的任務
- Block:被阻塞的任務
- Time Waiting:計時等待的任務
- Terminated:終止的任務
2. 線程中wait和sleep的區別?
參考回答:
wait方法既釋放cpu,又釋放鎖。
sleep方法只釋放cpu,但是不釋放鎖。
3. 線程和進程的區別?
參考回答:
線程是CPU調度的最小單位,一個進程中可以包含多個線程,在Android中,一個進程通常是一個App,App中會有一個主線程,主線程可以用來操作界面元素,如果有耗時的操作,必須開啓子線程執行,不然會出現ANR,除此以外,進程間的數據是獨立的,線程間的數據可以共享。
線程池
4. 與新建一個線程相比,線程池的特點?
參考回答:
- 節省開銷: 線程池中的線程可以重複利用。
- 速度快:任務來了就能開始,省去創建線程的時間。
- 線程可控:線程數量可空和任務可控。
- 功能強大:可以定時和重複執行任務。
5. 線程池中的幾個參數是什麼意思,線程池的種類有哪些?
參考回答:
線程池的構造函數如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
參數解釋如下:
- corePoolSize:核心線程數量,不會釋放。
- maximumPoolSize:允許使用的最大線程池數量,非核心線程數量,閒置時會釋放。
- keepAliveTime:閒置線程允許的最大閒置時間。
- unit:閒置時間的單位。
- workQueue:阻塞隊列,不同的阻塞隊列有不同的特性。
線程池分爲四個類型:
- CachedThreadPool:閒置線程超時會釋放,沒有閒置線程的情況下,每次都會創建新的線程。
- FixedThreadPool:線程池只能存放指定數量的線程池,線程不會釋放,可重複利用。
- SingleThreadExecutor:單線程的線程池。
- ScheduledThreadPool:可定時和重複執行的線程池。
- 線程池的工作流程?
參考回答:
鎖
7. 死鎖觸發的四大條件?
參考回答:
- 互斥鎖
- 請求與保持
- 不可剝奪
- 循環的請求與等待
8. synchronized和Lock的區別?
參考回答:
- synchronized是Java中的關鍵字,是Java的內置實現;Lock是Java中的接口。
- synchronized遇到異常會釋放鎖;Lock需要在發生異常的時候調用成員方法Lock#unlock()方法。
- synchronized是不可以中斷的,Lock可中斷。
- synchronized不能去嘗試獲得鎖,沒有獲得鎖就會被阻塞; Lock可以去嘗試獲得鎖,如果未獲得可以嘗試處理其他邏輯。
- synchronized多線程效率不如Lock,不過Java在1.6以後已經對synchronized進行大量的優化,所以性能上來講,其實差不了多少。
線程間通信
9. notify和notifyAll方法的區別?
參考回答:
notify隨機喚醒一個線程,notifyAll喚醒所有等待的線程,讓他們競爭鎖。
10. volatile的原理?
參考回答:
- 可見性
如果對聲明瞭volatile的變量進行寫操作的時候,JVM會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫入到系統內存。
多處理器的環境下,其他處理器的緩存還是舊的,爲了保證各個處理器一致,會通過嗅探在總線上傳播的數據來檢測自己的數據是否過期,如果過期,會強制重新將系統內存的數據讀取到處理器緩存。
- 有序性
Lock前綴的指令相當於一個內存柵欄,它確保指令排序的時候,不會把後面的指令拍到內存柵欄的前面,也不會把前面的指令排到內存柵欄的後面。
阻塞隊列
11. ConcurrentHashMap的原理?
參考回答:
數據結構的實現跟HashMap一樣,不做介紹。
JDK 1.8之前採用的是分段鎖,核心類是一個Segment,Segment繼承了ReentrantLock,每個Segment對象管理若干個桶,多個線程訪問同一個元素的時候只能去競爭獲取鎖。
JDK 1.8採用了CAS + synchronized,插入鍵值對的時候如果當前桶中沒有Node節點,使用CAS方式進行更新,如果有Node節點,則使用synchronized的方式進行更新。