java面試-synchronized底層實現機制


一、synchronized的三種應用方式

1、修飾實例方法,鎖是當前實例對象,進入同步代碼前要獲得當前實例的鎖

/**
 * synchronized修飾實例方法,當前線程的鎖是實例對象accountingSync
 * 當一個線程正在訪問一個對象的synchronized實例方法,那么其他線程不能訪問該對象的其他synchronized方法
 * 一個對象只有一把鎖
 */
public class AccountingSync implements Runnable {

    static AccountingSync accountingSync = new AccountingSync();
    //共享資源
    static int i = 0;
    static int j = 0;

    public synchronized void increase() {
        i++;
    }

    @Override
    public void run() {
        for(int i =0;i<1000000;i++){
            synchronized (this){
                increase();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(accountingSync);
        Thread thread2 = new Thread(accountingSync);
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }
}  
/**
 * thread1訪問實例對象obj1的synchronized方法,thread2訪問實例對象obj1的synchronized方法
 * 這樣是允許的,因為兩個實例對象鎖並不相同。
 * 此時如果兩個線程操作數據非共享,線程安全有保證,如果數據共享,線程安全無法保證
 *
 */
public class AccountingSyncBad implements Runnable {

    static int i = 0;

    public synchronized void increase() {
        i++;
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++) {
            increase();
        }
    }


    public static void main(String[] args) throws InterruptedException{
        //new新實例
        Thread thread1 = new Thread(new AccountingSyncBad());
        //new新實例
        Thread thread2 = new Thread(new AccountingSyncBad());
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }
}   

2、修飾靜態方法,鎖是當前類的 class對象,進入同步代碼前要獲得當前類對象的鎖 

public class AccountingSyncClass implements Runnable{

    static int i = 0;

    /**
     * synchronized作用於靜態方法,鎖是當前class對象
     */
    public static synchronized void increase() {
        i++;
    }

    /**
     *  increase4Obj方法是實例方法,其對象鎖是當前實例對象,
     *  如果別的線程調用該方法,將不會產生互斥現象,畢竟鎖對象不同,
     *  但我們應該意識到這種情況下可能會發現線程安全問題(操作了共享靜態變量i)。
     */
    public synchronized void increase4Obj(){
        i++;
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++) {
            increase();
//            increase4Obj();
        }
    }


    public static void main(String[] args) throws InterruptedException{
        //new新實例
        Thread thread1 = new Thread(new AccountingSyncClass());
        //new新實例
        Thread thread2 = new Thread(new AccountingSyncClass());
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(i);
    }
}  

3、修飾代碼塊

synchronized(this) 鎖是當前實例對象,
synchronized(AccountingSync.class) 鎖是class對象

二、synchronized代碼塊底層原理

synchronized代碼塊是由一對monitorenter和monitorexit指令實現的,Monitor對象是同步的基本實現單元。

現代java虛擬機對sychronized進行了優化,引入了偏斜鎖、輕量級鎖、重量級鎖

 

三、java虛擬機對Synchronized的優化

JVM優化synchronized運行的機制,當JVM檢測到不同的競爭情況時,會自動切換到適合的鎖實現

1、當沒有競爭出現時,默認會使用偏斜鎖JVM 會利用 CAS操作,在對象頭上的Mark Word部分設置線程ID,以表示這個對象偏向於當前線程,所以並不涉及真正的互斥鎖。這樣做的假設是基於在很多應用場景中,大部分對象生命周期中最多會被一個線程鎖定,使用偏斜鎖可以降低無競爭開銷。

2、有競爭出現時,當有另外的線程試圖鎖定某個已經被偏斜鎖鎖定的對象,jvm就會撤銷revoke偏斜鎖,並切換到輕量級鎖。輕量級鎖依賴CAS操作Mark Word來試圖獲取鎖,如果成功,就使用輕量級鎖,否則繼續升級未重量級鎖

PS:鎖降級也是存在的,當JVM進入SafePoint安全點的時候,會檢查是否有閑置的Monitor,然后試圖進行降級。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM