目錄
1 jvm內存模型
java內存模型規定所有變量都存在主內存中,主內存是所有線程共享的內存,但是線程對內存數據的操作(讀寫)是在工作內存中進行的,先將變量從
主內存拷貝到工作內存,然後進行操作,然後在將變量寫回主內存,不能對變量直接進行操作。
1.1 可見性
可見性是指當一個線程修改了共享變量的值,其他線程能夠立即得知這個修改。Java內存模型是通過在變量修改後將新值同步回主內存,
每次讀取變量都從主內存中讀取來實現可見性的
volatile:volatile的特殊規則保證了新值能立即同步到主內存,以及每次使用前立即從主內存刷新。
synchronized:synchronized關鍵字在釋放鎖之前,必須先把此變量同步回主內存中(執行store、write操作)。
final:被final修飾的字段在構造器中一旦初始化完成,並且構造器沒有把“this”的引用傳遞出去,那在其他線程中就能看見final字段的值
1.2 原子性
一個操作或多個操作要麼全部執行,且執行的過程不會被任何因素打斷,要麼就都不執行。
通過synchronized關鍵字定義同步代碼塊或者同步方法保障原子性。
通過Lock接口保障原子性。
通過Atomic類型保障原子性。
1.3 有序性
計算機在執行程序時,爲了提高性能,編譯器和處理器往往會對指令做重排,一般分爲以下3種
源碼->編譯器優化重排->指令並行重排->內存系統重排->最終執行指令
volatile和synchronized兩個關鍵字來保證線程之間操作的有序性
單例模式指令重排問題
instance = new Singleton();
memory = allocated(); //1 分配內存空間
instance = (memory); // 2 初始化對象
instance = memory; // 3 設置instance指向剛分配的內存空間
2和3順序可能替換,如果先執行3的話則對象還未初始化完成
2 CAS機制
JUC同步類容器中最常用的就是Unsafe的CAS方法,比較並交換
/**
* var1:需要修改屬性的對象
* var2:需要修改對象屬性的內存地址偏移量。v通過ar1和var2兩個變量可以讀取到對象屬性當前值
* var4:修改對象屬性時候期待當前的值
* var5:將對象屬性替換成的目標值
* return:如果對象當前屬性和期待值相等,則賦值成功,返回true;否則複製失敗,返回false
*/
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
新建一個User類
@Data
public class User {
/**
* 這裏一定要是基本數據類型,否則設置會失敗。如果用Long類型,則CAS請用compareAndSwapObject
*/
private long id;
private static final Unsafe unsafe;
/**
* 屬性id的內存地址偏移量
*/
private static final long idOffset;
static {
try {
// 通過反射獲取Unsafe類
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
Field idField = User.class.getDeclaredField("id");
// 讀取屬性id的內存地址偏移量
idOffset = unsafe.objectFieldOffset(idField);
} catch (Exception e) {
throw new Error(e);
}
}
public boolean casId(long expect, long target) {
return unsafe.compareAndSwapLong(this, idOffset, expect, target);
}
}
測試代碼
@Test
public void test() {
long id = 10L;
User user = new User();
user.setId(id);
System.out.println(user.casId(1L, 15L));
System.out.println(user.getId());
System.out.println(user.casId(id, 15L));
System.out.println(user.getId());
}
結果:
false
10
true
15