java中实现线程互斥的关键词_简单的互斥同步方式——synchronized关键字详解
2. synchronized的原理和實現細節
2.1 synchronized可以用在那些地方
靜態方法,鎖對象為當前類的class對象,不用顯式指定
實例方法,鎖對象為當前實例對象,不用顯式指定
同步代碼塊,鎖對象為括號中指定的對象,必須顯式指定
被synchronized修飾的方法或者代碼塊,同一時刻只能有一個線程能夠訪問它。
2.2 synchronized是如何實現線程互斥訪問的
雖然對于同步方法和同步代碼塊的底層細節略有不同,但都可以這么理解:對于被synchronized關鍵字修飾的代碼區域,java虛擬機會在開始的位置插入monitorenter指令,而在結束和異常處插入monitorexit指令,每一個monitorenter指令必定有一個monitorexit指令與其配對。線程在執行到monitorenter指令時,將會嘗試獲取鎖對象的monitor(監視器),該線程將進入同步方法或同步代碼塊中,同時monitor被鎖定,防止被多個線程同時獲取。獲取monitor失敗的線程將被阻塞在同步塊或同步方法的入口處,整個過程如下圖所示。
2.3 對象鎖的monitor信息存儲在哪
正如上面介紹的那樣,任何對象都可以作為synchronized代碼塊的鎖,而每個對象鎖都有個monitor與之關聯,線程通過獲取該monitor的所有權來實現對被同步代碼的互斥訪問。這個monitor信息存儲對象的對象頭重。
2.4 monitor信息在對象頭中的實現細節
對象鎖的monitor信息是存儲在該對象的對象頭中。對象頭的基本信息主要包括
由于鎖信息是在Mark Word中,我們繼續看看Mark Word到底包括哪些信息
內容比較多,不用強行記憶,只要知道大概有些東西就行了。
3. synchronized的內存語義
3.1 synchronized的可見性分析
由JMM的happens-before規則可知,對同步鎖的釋放happens-before于對同步鎖的獲取,因此在A線程釋放鎖之前對同步區域內共享變量作的修改,另一個線程B獲取鎖后將能夠馬上看到這種變化。它實現的原理和volatile類似
A線程釋放鎖時,JMM會把該線程對應的工作內存中的共享變量刷新到主存中
B線程獲取鎖后,JMM會把該線程對應的工作內存中的共享變量置為無效,同步代碼塊中的共享變量必須從主存中獲取。
因此,如果共享變量只在同步代碼塊或者同步方法中被用到,那它是沒必要用volatile修飾的!
3.2 synchronized禁止指令重排嗎
那同步代碼塊會禁止指令重排嗎?答案是不會。但只要同步塊中的共享變量對其他線程不可見,那么我們就不必擔心它引起的副作用。下面就是一個典型的例子
靜態內部類的懶漢式實現
public class Singleton {
private Singleton() {
}
static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這是線程安全的實現方式,盡管INSTANCE變量沒有用volatile修飾,但我們不用擔心指令重排序帶來的線程不安全問題。因為
INSTANCE = new Singleton();
是在類初始化的過程中執行的.JVM在Class文件被加載后,被線程使用前會執行類的初始化。這個過程會通過一個初始化鎖進行同步(對于每一個類或接口,都有唯一的初始化鎖),也就是說只有一個線程能獲取該鎖并執行這段代碼,其他無法線程無法訪問到INSTANCE變量,故重排序對其他線程不可見,整個過程是線程安全的。
4.鎖的優化
為了減少頻繁獲得和釋放鎖的性能開銷,JVM做了一系列優化。鎖一共分為4種狀態,級別從低到高依次為:無鎖狀態,偏向鎖狀態,輕量級鎖狀態,重量級鎖狀態。鎖的狀態會隨著競爭的加劇逐漸升級,獲得和釋放鎖的性能開銷也逐漸加大,且鎖只能升級而不能降級。為什么需要這么設置,它們各自又有什么特點,什么時候會出發鎖的升級?
偏向鎖: 當線程第一次訪問同步塊時,鎖為偏向鎖,該線程將一直持有偏向鎖,直到有其他線程競爭該鎖,該線程才釋放鎖,偏向鎖也升級為輕量級鎖。持有偏向鎖的線程只在第一次獲取時會進行CAS同步,接下來的訪問和釋放鎖將不需要進行任何同步操作。
輕量級鎖:輕量級鎖在獲取鎖的時候會使用CAS操作將鎖對象頭的mark word替換到線程的棧幀中,釋放鎖則使用CAS替換回來。競爭線程不會阻塞,而是使用自旋的方式等待,同時輕量級鎖將升級為重量級鎖。
重量級鎖:獲取和釋放鎖更消耗性能,且競爭鎖失敗的線程將被阻塞。
下面是鎖升級時的狀態圖
5. 總結
synchronized可以起到獨占鎖的作用,使多線程互斥的訪問被其修飾的代碼。雖然這種串行化會極大影響執行速度,但是能很好的保證線程安全,是開發中最經常使用的多線程技術之一。synchronized和volatile一樣保證了多線程之間的可見性,又由于線程訪問時的獨占性對其他線程屏蔽了指令重排的細節,故而可以說是比volatile更重量級也更可靠的同步工具。
總結
以上是生活随笔為你收集整理的java中实现线程互斥的关键词_简单的互斥同步方式——synchronized关键字详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: conf.exe是什么文件的进程 安全吗
- 下一篇: ts 模板库文件_在ts文件中使用模板引