synchronized鎖了什麼

前言

synchronized翻譯爲中文的意思是同步的,它是Java中處理線程安全問題經常使用的關鍵字。也有人稱其爲同步鎖。既然是鎖,其必然有鎖的東西,下面先會簡單介紹一下synchronized,再經過一個示例代碼展現synchronized鎖了什麼。(這裏先提早透露答案synchronized鎖的是代碼)安全

介紹

定義

synchronized提供的同步機制確保了同一個時刻,被修飾的代碼塊或方法只會有一個線程執行ide

用法

synchronized能夠修飾方法和代碼塊:測試

  1. 修飾普通方法
  2. 修飾靜態方法
  3. 修飾代碼塊

根據修飾狀況,分爲對象鎖和類鎖:this

  1. 對象鎖:線程

    • 普通方法(等價於代碼塊修飾this
    • 代碼塊修飾的是是類的一個對象
  2. 類鎖翻譯

    • 類方法(等價於代碼塊修飾當前類Class對象)
    • 代碼塊修飾的是是類Class對象

原理

synchronized底層原理是使用了對象持有的監視器monitor)。可是同步代碼塊和同步方法的原理存在一點差別:code

  • 同步代碼塊是使用monitorentermonitorexit指令實現的
  • 同步方法是由方法調用指令讀取運行時常量池中方法的ACC_SYNCHRONIZED 標識隱式實現,實際上仍是調用了monitorentermonitorexit指令

測試示例

計數器

一個特殊的計數器,自增方法increase()synchronized修飾,而獲取當前值方法getCurrent()則沒有被synchronized修飾。對象

/**
 * 計數器
 * @author RJH
 * create at 2019-03-13
 */
public class Counter {
    /**
     * 全局對象,總數
     */
    private static int i = 0;

    /**
     * 自增
     * @return
     */
    public synchronized int increase() {
        try {
            //使用休眠讓結果更明顯
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ++i;
    }

    /**
     * 獲取當前值
     * @return
     */
    public int getCurrent() {
        return i;
    }
}

測試代碼

使用自增線程和獲取當前值的線程來驗證synchronized鎖的是代碼,而不是全局變量get

/**
 * synchronized鎖了什麼
 * @author RJH
 * create at 2019-03-02
 */
public class LockWhatTest {

    public static void main(String[] args) {
        Counter counter =new Counter();
        IncreaseThread increaseThread1=new IncreaseThread(counter);
        IncreaseThread increaseThread2=new IncreaseThread(counter);
        GetThread getThread=new GetThread(counter);
        increaseThread1.start();
        increaseThread2.start();
        //直到increaseThread的線程啓動才執行下一步
        while (increaseThread1.getState().compareTo(Thread.State.NEW)==0 && increaseThread1.getState().compareTo(Thread.State.NEW)==0){

        }
        getThread.start();
    }

    /**
     * 自增線程
     */
    static class IncreaseThread extends Thread{

        private Counter counter;

        public IncreaseThread(Counter counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            System.out.println("After increase:" + counter.increase()+",trigger time:"+System.currentTimeMillis());
        }
    }

    /**
     * 獲取當前值的線程
     */
    static class GetThread extends Thread{

        private Counter counter;

        public GetThread(Counter counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            System.out.println("Current:"+ counter.getCurrent()+",trigger time:"+System.currentTimeMillis());
        }
    }

}

執行結果

Current:0,trigger time:1552487003845
After increase:1,trigger time:1552487008846
After increase:2,trigger time:1552487013848

結果分析

從測試結果能夠得知同步

  1. 在兩個自增線程啓動後,獲取當前值的線程才啓動,可是獲取當前值的線程是先被執行完成了。
  2. 根據自增線程執行完成的時間戳間隔能夠得知,兩個自增線程是依次執行的。

從而能夠證實

  1. synchronized並非鎖定方法內訪問的變量
  2. synchronized鎖定的是同一個監視器對象監視的代碼
相關文章
相關標籤/搜索