主頁 >  其他 > Java并發編程-synchronized鎖優化

Java并發編程-synchronized鎖優化

2021-02-06 10:20:31 其他

目錄

  • 1.小故事
  • 2.輕量級鎖
  • 3.鎖膨脹
  • 4.自旋優化
  • 5.偏向鎖
    • 5.1.概述
    • 5.2.偏向鎖狀態
    • 5.3.偏向鎖撤銷
      • 5.3.1.呼叫物件hashCode
      • 5.3.2.其它執行緒使用物件
      • 5.3.3.呼叫wait/notify
    • 5.4.批量重偏向
    • 5.5.批量撤銷
  • 6.其它優化
    • 6.1. 減少上鎖時間
    • 6.2.減少鎖的粒度
    • 6.3.鎖粗化
    • 6.4.鎖消除
    • 6.5. 讀寫分離

關于synchronized底層作業原理在上一節介紹過,本節在上節的基礎上講解synchronized優化

1.小故事

synchronized作業方式是讓每個物件關聯一個monitor,monitor鎖是由作業系統提供的,要使用它成本較高,如果是每次進入synchronized的話需要獲得一個monitor鎖,那么就需要很大的開銷,從Java6開始對synchronized獲取鎖的方式進行了優化,除了可以使用輕量級鎖,還可以使用偏向級鎖來進行優化,

故事角色

  • 老王 - JVM

  • 小南 - 執行緒

  • 小女 - 執行緒

  • 房間 - 物件

  • 房間門上 - 防盜鎖 - Monitor

  • 房間門上 - 小南書包 - 輕量級鎖

  • 房間門上 - 刻上小南大名 - 偏向鎖

  • 批量重刻名 - 一個類的偏向鎖撤銷到達 20 閾值

  • 不能刻名字 - 批量撤銷該類物件的偏向鎖,設定該類不可偏向

小南要使用房間保證計算不被其它人干擾(原子性),最初,他用的是防盜鎖,當背景關系切換時,鎖住門,這樣,即使他離開了,別人也進不了門,他的作業就是安全的,

但是,很多情況下沒人跟他來競爭房間的使用權,小女是要用房間,但使用的時間上是錯開的,小南白天用,小女晚上用,每次上鎖太麻煩了,有沒有更簡單的辦法呢?

小南和小女商量了一下,約定不鎖門了,而是誰用房間,誰把自己的書包掛在門口,但他們的書包樣式都一樣,因此每次進門前得翻翻書包,看課本是誰的,如果是自己的,那么就可以進門,這樣省的上鎖解鎖了,萬一書包不是自己的,那么就在門外等,并通知對方下次用鎖門的方式,

后來,小女回老家了,很長一段時間都不會用這個房間,小南每次還是掛書包,翻書包,雖然比鎖門省事了,但仍然覺得麻煩,

于是,小南干脆在門上刻上了自己的名字:【小南專屬房間,其它人勿用】,下次來用房間時,只要名字還在,那么說明沒人打擾,還是可以安全地使用房間,如果這期間有其它人要用這個房間,那么由使用者將小南刻的名字擦掉,升級為掛書包的方式,(偏向鎖,偏向某個物件)

同學們都放假回老家了,小南就膨脹了,在 20 個房間刻上了自己的名字,想進哪個進哪個,后來他自己放假回老家了,這時小女回來了(她也要用這些房間),結果就是得一個個地擦掉小南刻的名字,升級為掛書包的方式,老王覺得這成本有點高,提出了一種批量重刻名的方法,他讓小女不用掛書包了,可以直接在門上刻上自己的名字

后來,刻名的現象越來越頻繁,老王受不了了:算了,這些房間都不能刻名了,只能掛書包.

2.輕量級鎖

輕量級鎖使用場景:如果一個物件雖然有多執行緒訪問,但多執行緒訪問的時間是錯開的(也就是沒有競爭),那么可以使用輕量級鎖來優化.如果發生了競爭,那么鎖會升級為重量級鎖,
輕量級鎖對使用者是透明的,語法依然是synchronized
假設有兩個方法同步塊,利用同一個物件加鎖

static final Object obj = new Object();
 
public static void method1(){
    synchronized(obj){
        // 同步塊A
        method2();
    }
}
 
public static void method2(){
    synchronized(obj){
        // 同步塊 B
    }
}

創建鎖記錄(Lock record)物件,每個執行緒具備的堆疊幀都會包含一個所記錄的結構,內部可以存盤鎖定物件的Mark Vord;

在這里插入圖片描述
Lock Record中,Object reference記錄鎖物件的參考地址,lock record 地址 00記錄物件的mark word,
Object中:
Mark Word物件頭:hashcode 哈希碼 Age:分代年齡 狀態位
Klass Word: 型別指標:
Object body:類的成員變數;

Thread-0堆疊幀中
Lock Record中,Object reference記錄鎖物件的參考地址,lock record 地址 00記錄物件的mark word

其中,markword結構如下:
在這里插入圖片描述

鎖記錄中Object reference指向鎖物件,并嘗試用cas替換Object的mark word,將mark word的值存入所記錄

將鎖記錄資料與物件頭資料 進行交換,表示加鎖;

00表示的是一種輕量級鎖狀態,01表示無鎖狀態;見上圖;
在這里插入圖片描述

如果cas替換成功,物件頭中存盤了鎖記錄地址和狀態00,表示由該執行緒給物件加鎖,如下圖:

在這里插入圖片描述

如果cas失敗,有兩種情況

  • 如果是其他執行緒已經持有了該Object的輕量級鎖,這時表明有競爭,進入鎖膨脹程序(下一節);
  • 如果是自己執行了synchronized鎖重入,那么再添加一條Lock Record作為重入的計數
    也即第一次對它加了鎖,第二次又對它加了鎖,如上面的method1里面對obj加了鎖,呼叫method2方法,又對obj加了鎖,此時又產生了一個堆疊幀,這個堆疊幀里面又進入了synchronized,也會創建鎖記錄,objectreferce指向剛開要鎖定的物件,然后進行cas交換的操作,不過這次交換失敗了, 因為剛才已經由自己把后兩位改成了00,但是它知道這是當前執行緒中另外一條鎖記錄,這種情況叫做synchronized鎖沖入,也就是自己執行緒又一次給同一個物件加鎖了,這種情況再加一個lock record,只不過資料部分存入的是鎖重入的計數,
    在這里插入圖片描述

當退出synchronized代碼塊(解鎖時)如果有取值為null的鎖記錄,表示有重入,這時重置鎖記錄,表示重入計數減一
在這里插入圖片描述
當退出synchronized代碼塊(解鎖時)鎖記錄的值不為null,這時使用cas將Mark Word的值恢復給物件頭

成功,則解鎖成功
失敗,說明輕量級鎖進行了鎖膨脹已經升級為重量級鎖,進入重量級鎖解鎖流程

3.鎖膨脹

如果在嘗試加輕量級鎖的程序中,CAS操作無法成功,這時一種情況就是有其他執行緒為此物件加上了輕量級鎖(有競爭),這時候需要進行鎖膨脹,將輕量級鎖變為重量級鎖

static Object obj = new Object();
 
public static void method1(){
    synchronized(obj){
        // 同步塊
    }
}

當Thread-1進行輕量級加鎖時,Thread-0已經對該物件加了輕量級鎖
在這里插入圖片描述
這時Thread-1加輕量級鎖失敗(cas操作失敗),進入鎖膨脹流程

  • 即為Object物件申請Monitor鎖,讓Object指向重量級鎖地址
  • 然后自己進入Monitor的EntryList BLOCKED
    在這里插入圖片描述
    重量級后面兩位是00
    當Thread-0退出同步塊解鎖時,使用cas將Mark Word的值恢復給物件頭,失敗,這時候會進入重量級解鎖流程,即按照Monitor地址找到Monitor物件,設定Owner為null,喚醒EntryList中BLOCKED執行緒;

4.自旋優化

重量級鎖競爭的時候,還可以使用自旋來進行優化,如果當前執行緒是自旋成功(即這時候將鎖執行緒已經退出了同步塊,釋放了鎖),這時當前執行緒就可以避免阻塞.

因為阻塞要法生產執行緒背景關系切換,是比較耗時的,用自旋的方式就可以避免執行緒背景關系切換的發生,
自旋適用于多核CPU的場景下,單核CPU沒有意義

自旋成功的情況

執行緒1(CPU1上)物件Mark執行緒2(CPU2)
-10(重量級鎖)-
訪問同步塊,獲取Monitor10(重量級鎖)重量鎖指標-
成功(加鎖)10(重量級鎖)重量鎖指標-
執行同步塊10(重量級鎖)重量鎖指標-
執行同步塊10(重量級鎖)重量鎖指標訪問同步塊,獲取Monitor
執行同步塊10(重量級鎖)重量鎖指標自旋重試
執行完畢10(重量級鎖)重量鎖指標自旋嘗試
成功解鎖01無鎖自旋嘗試
-10(重量鎖)重量鎖指標成功加鎖
-10(重量鎖)重量鎖指標執行同步塊

自旋失敗的情況

執行緒1(CPU1上)物件Mark執行緒2(CPU2)
-10(重量級鎖)-
訪問同步塊,獲取Monitor10(重量級鎖)重量鎖指標-
成功(加鎖)10(重量級鎖)重量鎖指標-
執行同步塊10(重量級鎖)重量鎖指標-
執行同步塊10(重量級鎖)重量鎖指標訪問同步塊,獲取Monitor
執行同步塊10(重量級鎖)重量鎖指標自旋重試
執行同步塊10(重量級鎖)重量鎖指標自旋重試
執行同步塊10(重量級鎖)重量鎖指標自旋重試
執行同步塊10(重量鎖)重量鎖指標阻塞
  • 在Java6之后自旋鎖是自適應的,比如物件剛剛的一次自旋操作成功過,那么認為這次自旋成功的可能性會高,就多自旋幾次;反之,就少自旋甚至不自旋,總之,比較智能,
  • 自旋會占用CPU時間,單核 CPU自旋就是浪費,多核CPU自旋才能發揮優勢*
  • Java7之后不能控制是否開啟自旋功能

5.偏向鎖

5.1.概述

輕量級鎖在沒有競爭時,(就自己這個執行緒),每次重入仍然執行CAS操作.

Java6中引入了偏向鎖來做進一步優化;只有第一次使用CAS將執行緒ID設定到物件的Mark Word頭,之后發現這個執行緒ID是自己的就表示沒有競爭,不用重新CAS,以后只要不發生競爭,這個物件就歸該執行緒所有

例如:

static final Object obj = new Object();
 
public static void m1(){
    synchronized(obj){
        // 同步塊 A
        m2();
    }
}
 
public static void m2(){
//同一個執行緒再次對同一個物件加鎖,此時m1還未釋放鎖
    synchronized(obj){
        // 同步塊 B
        m3();
    }
}
 
public static void m3(){
//同一個執行緒再次對同一個物件加鎖,此時m1,m2還未釋放鎖
    synchronized(obj){
        // 同步塊 C
        
    }
}

在這里插入圖片描述
優化,類似把名字刻在房間門口
在這里插入圖片描述
即這個物件就偏向這個執行緒,

5.2.偏向鎖狀態

在這里插入圖片描述
如圖,biased_lock表示是否啟用了偏向鎖,如果是0,沒有啟用偏向鎖,1就表明偏向狀態,
如果是偏向狀態,存盤的就是執行緒id,epoch在后面會用到,
一個物件創建時:

如果開啟了偏向鎖(默認開啟),那么物件創建后,markword值為0x05即最后3位101,這時它的Thread,epoch,age都為0
偏向鎖默認是延遲的,不會在程式啟動時立即生效,如果想避免延遲,可以加VM引數-xx:BiaseLockingStartupDelay=0來禁用延遲
如果沒有開啟偏向鎖,那么物件創建后,markword值為0x01即最后3位位001,這時它的hashcode,age,都為0,第一次用到hashcode時才會賦值

偏向鎖的延遲啟動時間
偏向鎖默認是在JVM啟動4s后再初始化偏向鎖,可用如下引數修改啟動時間,設為0則表示立即啟用,之所以這么設計是因為JVM啟動的時候,如果立即啟動偏向,有可能會因為執行緒競爭太激烈導致產生太多安全點掛起,
-XX:BiasedLockingStartupDelay=0

如果多執行緒環境下, 資源經常需要競爭使用,那么這個時候就不適合用偏向鎖了,在測驗代碼運行時在添加VM引數-XX:UserBiasedLocking禁用偏向鎖,

5.3.偏向鎖撤銷

5.3.1.呼叫物件hashCode

呼叫了物件的hashCode,但偏向鎖的物件MarkWord中存盤的是執行緒id,如果呼叫hashCode會導致偏向鎖被撤銷

  • 輕量級鎖會在鎖記錄中記錄hashCode
  • 重量級鎖會在Monitor中記錄hashCode
    在呼叫hashCode后使用偏向鎖,記得去掉-XX:UseBiasedLocking

5.3.2.其它執行緒使用物件

當有其他執行緒使用偏向鎖物件時,會將偏向鎖升級為輕量級鎖
注意:我們要讓兩個執行緒交錯開,必須是t1執行緒先解鎖,t2再去加鎖,如果有競爭那么就是重量級鎖,輕量級鎖和偏向鎖的前提是執行緒訪問物件時必須是錯開的,我們可以使用wait/notify來實作這樣的效果,

private static void test2() throws InterruptedException {
    Dog d = new Dog();
    Thread t1 = new Thread(() -> {
        synchronized (d) {
            log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
        }
        synchronized (TestBiased.class) {
    TestBiased.class.notify();
}
// 如果不用 wait/notify 使用 join 必須打開下面的注釋
// 因為:t1 執行緒不能結束,否則底層執行緒可能被 jvm 重用作為 t2 執行緒,底層執行緒 id 是一樣的
/*try {
        System.in.read();
    } catch (IOException e) {
        e.printStackTrace();
    }*/
}, "t1");
    t1.start();
    Thread t2 = new Thread(() -> {
        synchronized (TestBiased.class) {
    try {
        TestBiased.class.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
    log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }, "t2");
    t2.start();
    }
[t1] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101
[t2] - 00000000 00000000 00000000 00000000 00011111 01000001 00010000 00000101
[t2] - 00000000 00000000 00000000 00000000 00011111 10110101 11110000 01000000
[t2] - 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

5.3.3.呼叫wait/notify

public static void main(String[] args) throws InterruptedException {
    Dog d = new Dog();
Thread t1 = new Thread(() -> {
    log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    synchronized (d) {
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    try {
        d.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
        log.debug(ClassLayout.parseInstance(d).toPrintableSimple(true));
    }
}, "t1");
        t1.start();
    new Thread(() -> {
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
            synchronized (d) {
            log.debug("notify");
            d.notify();
    }
    }, "t2").start();
}

5.4.批量重偏向

如果物件雖然被多個執行緒訪問,但沒有競爭,這時偏向了執行緒 T1 的物件仍有機會重新偏向 T2,重偏向會重置物件的 Thread ID
當撤銷偏向鎖閾值超過 20 次后,jvm 會這樣覺得,我是不是偏向錯了呢,于是會在給這些物件加鎖時重新偏向至加鎖執行緒

private static void test3() throws InterruptedException {
	Vector<Dog> list = new Vector<>();
	Thread t1 = new Thread(() -> {
		for (int i = 0; i < 30; i++) {
			Dog d = new Dog();
			list.add(d);
			synchronized (d) {
				log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			}
		}
		synchronized (list) {
			list.notify();
		}
	}, "t1");
	t1.start();
	Thread t2 = new Thread(() -> {
		synchronized (list) {
			try {
				list.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		log.debug("===============> ");
		for (int i = 0; i < 30; i++) {
			Dog d = list.get(i);
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			synchronized (d) {
				log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			}
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
		}
	}, "t2");
	t2.start();
}
[t1] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t1] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - ===============>
[t2] - 0 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 0 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 0 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 1 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 1 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 2 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 2 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 2 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 3 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 3 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 3 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 4 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 4 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 4 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 5 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 5 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 5 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 6 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 6 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 6 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 7 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 7 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 8 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 8 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 8 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 9 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 9 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 9 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 10 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 10 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 10 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 11 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 11 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 11 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 12 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 12 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 12 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 13 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 13 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 13 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 14 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 14 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 14 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 15 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 15 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 15 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 16 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 16 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 16 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 17 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 17 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 17 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 18 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 18 00000000 00000000 00000000 00000000 00100000 01011000 11110111 00000000
[t2] - 18 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 19 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 20 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 21 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 22 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 23 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 24 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 25 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 26 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 27 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 28 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11100000 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101
[t2] - 29 00000000 00000000 00000000 00000000 00011111 11110011 11110001 00000101

5.5.批量撤銷

當撤銷偏向鎖閾值超過 40 次后,jvm 會這樣覺得,自己確實偏向錯了,根本就不該偏向,于是整個類的所有物件都會變為不可偏向的,新建的物件也是不可偏向的,

static Thread t1,t2,t3;
private static void test4() throws InterruptedException {
	Vector<Dog> list = new Vector<>();
	int loopNumber = 39;
	t1 = new Thread(() -> {
		for (int i = 0; i < loopNumber; i++) {
			Dog d = new Dog();
			list.add(d);
			synchronized (d) {
				log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			}
		}
		LockSupport.unpark(t2);
	}, "t1");
	t1.start();
	t2 = new Thread(() -> {
		LockSupport.park();
		log.debug("===============> ");
		for (int i = 0; i < loopNumber; i++) {
			Dog d = list.get(i);
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			synchronized (d) {
				log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			}
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
		}
		LockSupport.unpark(t3);
	}, "t2");
	t2.start();
	t3 = new Thread(() -> {
		LockSupport.park();
		log.debug("===============> ");
		for (int i = 0; i < loopNumber; i++) {
			Dog d = list.get(i);
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			synchronized (d) {
				log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
			}
			log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintableSimple(true));
		}
	}, "t3");
	t3.start();
	t3.join();
	log.debug(ClassLayout.parseInstance(new Dog()).toPrintableSimple(true));
}

6.其它優化

6.1. 減少上鎖時間

同步代碼塊中盡量短.,
上鎖時間短,競爭機會少,如果是交錯運行下,此時可以用輕量級鎖來優化,如果上鎖時間長,交錯機會就增加了,輕量級鎖就會升級為重量級鎖,所以盡量讓synchronized代碼塊盡可能短,

6.2.減少鎖的粒度

將一個鎖拆分為多個鎖提高并發度

  • ConcurrentHashMap(在陣列的鏈表頭上進行加鎖,如果hashtable就鎖住了整個的hashtable,如果我只鎖住了鏈表頭,加鎖的粒度就變小了,每次只鎖住了一個鏈表,其它鏈表讀寫操作不會受到任何影響)
  • LongAdder分為base和cells兩部分,沒有并發爭用的時候或者是cells陣列正在初始化時候,會使用CAS來累加值到base,有并發爭用,會初始化 cells 陣列,陣列有多少個 cell,就允 許有多少執行緒并行修改,后將陣列中每個 cell 累加,再加上 base 就是終的值
  • LinkedBlockingQueue 入隊和出隊使用不同的鎖,相對于LinkedBlockingArray只有一個鎖效率要高

6.3.鎖粗化

多次回圈進入同步塊不如同步塊內多次回圈 另外 JVM 可能會做如下優化,把多次 append 的加鎖操作 粗化為一次(因為都是對同一個物件加鎖,沒必要重入多次)

new StringBuffer().append("a").append("b").append("c");

6.4.鎖消除

JVM 會進行代碼的逃逸分析,例如某個加鎖物件是方法內區域變數,不會被其它執行緒所訪問到,這時候 就會被即時編譯器忽略掉所有同步操作

6.5. 讀寫分離

CopyOnWriteArrayList ConyOnWriteSet

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/257053.html

標籤:其他

上一篇:JAVASE小白學習筆記 簡單探索CAS演算法原理

下一篇:鼎陽SDS6204示波器的EPICS IOC除錯

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more