一. JVM 對 Java 的原生鎖做了哪些優化?
1. 自旋鎖
在線程進行阻塞的時候,先讓線程自旋等待一段時間,可能這段時間其它線程已經解鎖,這時就無需讓線程再進行阻塞操作了。 自旋默認次數是10次。
2. 自適應自旋鎖
自旋鎖的升級,自旋的次數不再固定,由前一次自旋次數和鎖的擁有者的狀態決定。
3. 鎖消除
在動態編譯同步代碼塊的時候,JIT編譯器借助逃逸分析技術來判斷鎖對象是否只被一個線程訪問,而沒有其他線程,這時就可以取消鎖了。
4. 鎖粗化
當JIT編譯器發現一系列的操作都對同一個對象反復加鎖解鎖,甚至加鎖操作出現在循環中,此時會將加鎖同步的范圍粗化到整個操作系列的外部。
鎖粒度:不要鎖住一些無關的代碼。
鎖粗化:可以一次性執行完的不要多次加鎖執行。
二. 為什么wait()、notify()和 notifyAll()必須在同步方法或者同步塊中被調用?
Java中,任何對象都可以作為鎖,并且 wait(),notify()等方法用于等待對象的鎖或者喚醒線程,在 Java 的線程中并沒有可供任何對象使用的鎖,所以任意對象調用方法一定定義在Object類中。
wait(), notify()和 notifyAll()這些方法在同步代碼塊中調用。
有的人會說,既然是線程放棄對象鎖,那也可以把wait()定義在Thread類里面啊,新定義的線程繼承于Thread類,也不需要重新定義wait()方法的實現。然而,這樣做有一個非常大的問題,一個線程完全可以持有很多鎖,你一個線程放棄鎖的時候,到底要放棄哪個鎖?當然了,這種設計并不是不能實現,只是管理起來更加復雜。
綜上所述,wait()、notify()和notifyAll()方法要定義在Object類中。
三. Java 如何實現多線程之間的通訊和協作?
可以通過中斷 和 共享變量的方式實現線程間的通訊和協作。
比如說最經典的生產者-消費者模型。當隊列滿時,生產者需要等待隊列有空間才能繼續往里面放入商品,而在等待的期間內,生產者必須釋放對臨界資源(即隊列)的占用權。因為生產者如果不釋放對臨界資源的占用權,那么消費者就無法消費隊列中的商品,就不會讓隊列有空間,那么生產者就會一直無限等待下去。因此,一般情況下,當隊列滿時,會讓生產者交出對臨界資源的占用權,并進入掛起狀態。然后等待消費者消費了商品,然后消費者通知生產者隊列有空間了。同樣地,當隊列空時,消費者也必須等待,等待生產者通知它隊列中有商品了。這種互相通信的過程就是線程間的協作。
Java中線程通信協作的最常見的兩種方式:
syncrhoized加鎖的線程的Object類的wait()/notify()/notifyAll()
ReentrantLock類加鎖的線程的Condition類的await()/signal()/signalAll()
通過管道進行線程間通信:
字節流;
字符流
四. Thread 類中的 yield 方法有什么作用?
yield()應該做的是讓當前運行線程回到可運行狀態,以允許具有相同優先級的其他線程獲得運行機會。因此,使用yield()的目的是讓相同優先級的線程之間能適當的輪轉執行。但是,實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程調度程序再次選中。
結論:yield()從未導致線程轉到等待/睡眠/阻塞狀態。在大多數情況下,yield()將導致線程從運行狀態轉到可運行狀態,但有可能沒有效果。
更多關于“Java培訓”的問題,歡迎咨詢千鋒教育在線名師。千鋒已有十余年的培訓經驗,課程大綱更科學更專業,有針對零基礎的就業班,有針對想提升技術的好程序員班,高品質課程助力你實現java程序員夢想。