java 同步锁_Java多线程:synchronized同步锁的使用和实现原理
生活随笔
收集整理的這篇文章主要介紹了
java 同步锁_Java多线程:synchronized同步锁的使用和实现原理
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
作用和用法
- 在多線程對共享資源進行并發訪問方面,JDK提供了synchronized關鍵字來進行線程同步,實現多線程并發訪問的線程安全。synchronized的作用主要體現在三個方面:(1)確保線程互斥地訪問同步代碼;(2)保證共享變量的線程可見性;(3)禁止指令重排。其中(2)和(3)相當于volatile關鍵字的作用。
- synchronized可以用在代碼的以下地方:
(1)靜態方法:將類對象自身作為monitor對象,對該類所有使用了sychronized修飾的靜態方法進行同步,即任何時候只能存在一個線程在調用該類的使用了synchronized修飾的靜態方法,其他調用了該類的使用了synchronized修飾的靜態方法的線程需要阻塞;
(2)普通成員方法:使用類的對象實例作為monitor對象,該類所有使用了synchronized修飾的成員方法,在任何時刻只能被一個線程訪問,其他線程需要阻塞;
(3)代碼塊:使用某個對象作為monitor對象,通常為一個普通的private成員變量,如private Object object = new Object();,這樣所有使用了該object對象的同步塊,在任何時候只能存在一個線程訪問。
- synchronized可以與monitor對象的wait,notify,notifyAll方法一起來使用,實現線程之間的通信,如實現生產者和消費者模型。其中多個線程共享一個monitor對象,在線程持有synchronized鎖時,才能調用monitor的wait,notify或者notifyAll,分別用于釋放monitor鎖,阻塞休眠,等待其他線程;通知和喚醒其中一個阻塞休眠的線程,讓該線程去獲取monitor鎖;通知所有阻塞休眠的線程去競爭monitor鎖。
- synchronized使用方便,無需顯示地在應用代碼中加鎖和解鎖,只需在對應的方法或者代碼塊中使用synchronized關鍵字修飾即可,由JVM自身實現自動地加鎖和釋放鎖。
- synchronized修飾的范圍越小,線程并發度越高,性能越好,所以通常使用同步代碼塊,而不是同步方法來縮小同步范圍,優化性能。
實現原理
JVM層面
- synchronized關鍵字是基于JVM提供的monitorenter和monitorexit字節碼指令,以及結合監視器monitor來實現的。
- 由上面的分析可知,synchronized關鍵字用在靜態方法,普通成員方法,代碼塊中,分別需要以類對象自身,類的對象實例,某個普通對象作為對應的monitor對象。
- 由JVM的相關知識可知,任何java類都需要編譯成class字節碼,然后加載到JVM當中去執行。而在編譯一個java類生成對應class字節碼時,當遇到sychronized關鍵字時,會在sychronized關鍵字所修飾的方法或者代碼塊的開始處:增加一個monitorenter字節碼指令,在方法或者代碼塊的結束處:增加monitorexit字節碼指令,即使用monitorenter和monitorexit字節碼指令包圍該方法或者代碼塊對應的字節碼。如下:
- 在類的成員方法中使用synchronized關鍵字:
- 反編譯該類對應的class字節碼文件:在成員方法method對應的字節碼周圍使用了monitorenter和monitorexit字節碼指令。
monitorenter和monitorexit指令的作用為:
- monitorenter的作用:所有線程共享該同步代碼和該對象關聯的監視器monitor,每個線程執行到monitorenter指令的時候,會檢查對應的monitor對象的計數是否為0,是則當前線程成為該monitor對象的owner,即鎖住該monitor對象了,并遞增該計數為1,之后該線程每調用一次使用了該monitor對象進行同步的方法,計數加一(所以synchronized也是可重入的);其他線程檢查到monitor對象的計數不為0,則知道該monitor對象已經被其他線程持有鎖住了,故當前線程會阻塞直到該monitor的計數重新變為0,則阻塞的線程們會繼續競爭成為該monitor的owner,從而可以訪問同步代碼。
- monitorexit的作用:當持有該monitor對象的線程每執行完一個同步代碼時(如對于成員方法,如果該線程調用了多個使用sychronized修飾的成員方法,則每個方法執行完執行一次monitor減一),將monitor的計數減一,當monitor對象的計數遞減到0時,則當前線程不再持有該monitor對象,其他阻塞的線程此時可以競爭成為該monitor的owner,成功的線程可以訪問同步代碼。
- 為什么monitor對象的wait,notify,notifyAll需要在synchronized同步代碼里面使用呢?首先需要理解以下概念:
- wait方法:釋放對象鎖,然后進入等待隊列;
- notify和notifyAll方法:從等待隊列被喚醒,放到同步隊列去競爭該對象鎖;
- 所以線程在執行wait,notify,notifyAll時需要依賴該監視器monitor,即該線程成為該監視器的owner,從而可以訪問synchronized包圍的同步代碼,這樣才能有權訪問該監視器對應的對象鎖,等待隊列和同步隊列。
操作系統層面
- 在操作系統層面,synchronized是基于操作系統的Metux Lock來實現的,而操作系統實現線程之間的切換是需要進行上下文切換的,即從用戶態切換到內核態,所以這也是synchronized相對來說成本較高,性能相對較低的原因。
總結
以上是生活随笔為你收集整理的java 同步锁_Java多线程:synchronized同步锁的使用和实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++基础11-类和对象之操作符重载1
- 下一篇: STL14-set/multiset容器