一,、作用 1. 多線程同步代碼,保證方法或者代碼塊在運行時,,同一時刻只有一個線程可以進入到臨界區(qū)(互斥性) 2. 保證線程間共享變量的修改及時可見(可見性) a. 當(dāng)線程獲取鎖時,,線程的本地變量無效,需要從主存中獲取共享變量的值 b. 線程釋放鎖時,線程的本地變量被刷新到主存中 3. 有效解決重排序問題(有序性) 二,、用法 1. Java中的每個對象都可以作為鎖,,獲取的鎖都是對象 2. 修飾函數(shù),即普通同步方法,,鎖是當(dāng)前類的實例對象 public void synchronized A(){} 3. 靜態(tài)同步方法,,鎖是當(dāng)前類的class對象 public static void synchronized A(){} 4. 修飾函數(shù)內(nèi)的語句塊,即同步代碼塊,,鎖是括號中的對象 synchronized(obj){} 5. 每個對象只有一個鎖(lock)與之關(guān)聯(lián) 6. 作用域 a. 某個對象實例內(nèi)的方法,,不同對象的實例內(nèi)的方法不相干擾,其他線程可以同時訪問相同類的其他對象實例中的synchronized方法 b. 某個類的范圍,,一般是靜態(tài)方法,,可以防止多個線程同時訪問相同類中的synchronized方法 三、原理 1. java示例代碼,,同步靜態(tài)方法 public class SynchronizedTest { private static Object object = new Object(); public static void main(String[] args) throws Exception{ synchronized(object) { } } public static synchronized void m() {} } 2. 代碼->字節(jié)碼->反編譯后的代碼 public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic #2 // Field object:Ljava/lang/Object 3: dup 4: astore_1 5: monitorenter //監(jiān)視器進入,,獲取鎖 6: aload_1 7: monitorexit //監(jiān)視器退出,釋放鎖 8: goto 16 11: astore_2 12: aload_1 13: monitorexit 14: aload_2 15: athrow 16: return public static synchronized void m(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 9: 0 3. 同步代碼塊使用monitorenter和monitorexit指令實現(xiàn) 4. 同步方法使用修飾符ACC_SYNCHRONIZED實現(xiàn) 5. 無論哪種實現(xiàn),,本質(zhì)上都是對monitor的獲取,,這個過程是互斥的 四、鎖的狀態(tài) 1. 對象頭: a. 對象在內(nèi)存中分為3部分:對象頭,、實例數(shù)據(jù),、對齊填充 b. 對象頭可以記錄對象的狀態(tài):偏向鎖、輕量級鎖,、重量級鎖 2. monitor: a. 線程私有的數(shù)據(jù)結(jié)構(gòu),,每個線程有一個monitor列表 b. JVM記錄獲取monitor鎖的線程唯一id,確保一次只有一個線程執(zhí)行 3. 偏向鎖狀態(tài): a. 如果一個線程獲取了鎖,,那么鎖就進入了偏向狀態(tài) b. 當(dāng)這個線程再次請求鎖時,,不需要再做同步,就可以獲取鎖,,避免了申請鎖的操作,,優(yōu)化了性能 c. 適用于沒有激烈競爭鎖的情況,不僅沒有多線程競爭,,而且總是由同一個線程獲取鎖 4. 輕量級鎖狀態(tài): a. 獲取偏向鎖失敗后,,會膨脹為輕量級鎖 b. 適用于交替執(zhí)行同步塊的情況 5. 重量級鎖狀態(tài): a. 獲取輕量級鎖失敗后,會膨脹為重量級鎖 b. 此時所有線程都會被鎖住,,當(dāng)前獲取鎖的線程釋放后,,其他線程才被喚醒 五、等待通知機制,,即wait(),notify(),notifyall()進行線程間通信 1. 代碼 public class WaitNotify { static boolean flag = true; static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread A = new Thread(new Wait(), "wait thread"); A.start(); TimeUnit.SECONDS.sleep(2); Thread B = new Thread(new Notify(), "notify thread"); B.start(); } static class Wait implements Runnable { @Override public void run() { synchronized (lock) { while (flag) { try { System.out.println(Thread.currentThread() " flag is true"); lock.wait(); } catch (InterruptedException e) { } } System.out.println(Thread.currentThread() " flag is false"); } } } static class Notify implements Runnable { @Override public void run() { synchronized (lock) { flag = false; lock.notifyAll(); try { TimeUnit.SECONDS.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } } } 2. 使用wait(),,notify(),,notifyAll()時,需要先對對象加鎖 3. 調(diào)用wait()方法后,,線程會釋放鎖,,線程狀態(tài)RUNNING->WAITING,將線程移動到等待隊列 4. notify()/notifyAll()時,,等待線程不會立即從wait()返回,,需要當(dāng)前線程釋放鎖之后,才有機會獲取鎖返回 5. notify()將一個等待隊列里的線程,,移動到同步隊列,,線程狀態(tài)WAITING->BLOCKED 6. notifyAll()將所有等待隊列里的線程,移動到同步隊列,,線程狀態(tài)WAITING->BLOCKED 7. 線程只有獲取了鎖,,才能從wait()返回 |
|