實作
- 非靜态方法的代碼塊
按照Hotspot的實作來看,是在這個對象的對象頭位置(64位,前兩位 ##mark word)記錄了這個對象鎖的狀态。
public synchronized void m1() {
System.out.println(Thread.currentThread().getName() + " m1 start...");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m1 end");
}
- 靜态方法的代碼塊
這個類T在被類加載器加載到jvm記憶體中的時候,生成了一個特殊的類Class<T>。靜态方法的代碼塊鎖其實就是synchronized(T.class)
public synchronized static void m() { //這裡等同于synchronized(T.class)
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
引申問題:T.class 加載到記憶體中一定是單例麼?
并不一定是。如果使用的同一個ClassLoader那一定是,不同的ClassLoader不一定。
- 對象鎖
任何對象都需要先拿到O這個對象的鎖
public void m() {
synchronized(o) { //任何線程要執行下面的代碼,必須先拿到o的鎖
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
- 可重入鎖
同一個線程中,允許對同一個對象鎖多次。---必須是可重入鎖
synchronized void m1() {
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2");
}
synchronize的鎖更新
《沒錯,我就是廁所所長》
https://www.jianshu.com/p/b43b7bf5e052
https://www.jianshu.com/p/16c8b3707436
偏向鎖
當一個線程來了之後,隻是在MarkWord上面标記了狀态(線程的id值),其實并沒有真正的加鎖。下次還是這個線程通路的時候,發現線程id是一樣的,那麼就直接使用,不需要加鎖。
自旋鎖
在一個線程持有偏向鎖的時候,另一個線程也要來申請使用這個鎖,那麼就鎖更新進入了自旋鎖。
自旋鎖類似于一直while(true)循環,一直在檢視鎖狀态,是在核心态進行的。###自旋鎖速度快。
對應的線程不停的在CPU上運作,假設1W個線程争搶一把鎖,有一個1個獲得了鎖,剩下的9999個都在那裡自旋,是以CPU壓力是非常大的。 ###自旋鎖會占用CPU
是以自旋鎖适合-->線程數少,并且不會長時間運作的線程
重量級鎖(自旋10次之後,更新為重量級鎖)
重量級鎖是進入os,進入等待隊列,這個時候線程不再占CPU,進入了等待隊列。由CPU決定是什麼時候運作。
是以重量級鎖适合-->線程數量多,并且會長時間運作的線程