synchronized和volatile關鍵字的用法和區別
1. synchronized關鍵字
1. 介紹
synchronized關鍵字是java提供的鎖機制, 主要解決線程同步的問題, 那么它可以修飾方法和同步代碼塊, 鎖使用的范圍一般情況是越小越好
2. 原理
synchronized是通過對象內部的一個叫做監視器鎖(monitor)來實現的. 但是監視器鎖本質又是依賴於底層的操作系統的互斥鎖(Mutex Lock)來是實現的. 而操作系統實現線程之間的切換這就需要從用戶態轉換到核心態, 這個成本非常高, 狀態之間的轉換需要相對比較長的時間, 這就是為什么synchronized效率低的原因. 這種依賴於操作系統互斥鎖鎖是吸納的鎖, 我們稱之為重量級鎖
3. synchronized的兩種用法
public class ATest {
// 作用在方法上的synchronized關鍵字
public synchronized void method1() {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
// 作用在代碼塊的synchronized關鍵字
public void method2() {
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
}
4. 單例模式
public class Singleton {
private static Singleton instance = null;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null) {
synchronzied(Singleton.class) {
if(instance == null) {
instance = new Singleton(); //非原子操作
}
}
}
return instance;
}
}
2. volatile關鍵字
參考: https://www.cnblogs.com/dolphin0520/p/3920373.html
正確使用 volatile 變量的條件
您只能在有限的一些情形下使用 volatile 變量替代鎖。要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:
- 對變量的寫操作不依賴於當前值。
- 該變量沒有包含在具有其他變量的不變式中。
實際上,這些條件表明,可以被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。
第一個條件的限制使 volatile 變量不能用作線程安全計數器。雖然增量操作(x++
)看上去類似一個單獨操作,實際上它是一個由讀取-修改-寫入操作序列組成的組合操作,必須以原子方式執行,而 volatile 不能提供必須的原子特性。實現正確的操作需要使 x
的值在操作期間保持不變,而 volatile 變量無法實現這點。(然而,如果將值調整為只從單個線程寫入,那么可以忽略第一個條件。)
3. synchronized和volatile的區別
一旦一個共享變量(類的成員變量、類的靜態成員變量)被volatile修飾之后,那么就具備了兩層語義:
1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是
立即可見的。
2)禁止進行指令重排序。
volatile本質是在告訴jvm當前變量在寄存器(工作內存)中的值是不確定的,需要從主存中讀取;
synchronized則是鎖定當前變量,只有當前線程可以訪問該變量,其他線程被阻塞住。
1.volatile僅能使用在變量級別;
synchronized則可以使用在變量、方法、和類級別的
2.volatile僅能實現變量的修改可見性,並不能保證原子性;
synchronized則可以保證變量的修改可見性和原子性
3.volatile不會造成線程的阻塞;
synchronized可能會造成線程的阻塞。
4.volatile標記的變量不會被編譯器優化;
synchronized標記的變量可以被編譯器優化