java面試 synchronized關鍵字

<h1>一. synchronized 同步代碼關鍵字</h1>
此關鍵字為java中非常重要的關鍵字類型,當用它來修飾一個方法或者代碼塊的時候,能夠保證所修飾的方法或者代碼塊在同一時刻最多只有一個線程在執行該段代碼。
1) 當兩個或者多個并發線程同時訪問一個object中的synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行,其他線程必須要等到當前線程執行完這個代碼塊之后才能繼續執行該代碼塊。
2) 然而,當一個線程訪問object中的synchronized(this)同步代碼塊時,其他線程仍然可以調用object中的其它非synchronized(this)同步代碼塊。
3) 尤其關鍵的是,當一個線程訪問object中的一個synchronized(this)同步代碼塊時,其他線程對于object中的其它synchronized(this)同步代碼塊的訪問,都將被阻塞。
4) 第3條中的說明同樣適用于其它同步代碼塊,也就是說,當一個線程訪問object中的一個synchronized(this)同步代碼塊時,它就獲得了整個object的對象鎖,因此,其他所有線程對于object的所有同步代碼塊的訪問都將被暫時阻塞,直至當前線程執行完這個代碼塊。
5) 上述的規則對于其他對象鎖也同樣適用。

對于synchronized關鍵字的個人理解
synchronized關鍵字,包括兩種用法,一種是synchronized方法,另一種是synchronized塊。

1. synchronized方法###

如果我們想要聲明一個synchronized方法,只需要在聲明方法的時候,加入synchronized關鍵字即可,如:

public synchronized void method2()

每一個類實例都對應一把鎖,每個synchronized方法的執行都必須要獲得調用該方法的類的實例的鎖,才能執行,否則,線程就會阻塞。而方法一旦開始執行,就會獨占該鎖,直到方法執行完成返回后釋放該鎖,然后阻塞的線程就獲取鎖,然后調用方法執行。
這種機制確保了同一時刻對于每一個類的實例來講,只有一個synchronized方法處于正在執行狀態,因為同一時間最多只能有一個線程獲取到類的實例的鎖。這能確保類成員變量的訪問沖突問題,前提是我們將所有可能沖突的方法聲明為synchronized方法。
在java中,不僅僅是類的實例,類也有鎖,我們可以將類的靜態變量聲明上添加synchronized關鍵字,這樣就能控制線程對于類靜態成員變量的訪問。
缺點:將方法聲明為synchronized會影響效率。例如,如果我們想線程類的run()方法定義為synchronized的,則在線程的整個生命周期內,它都會一直運行,且它無法調用本類任何synchronized方法,因為一直占用鎖。所以我們可以將訪問成員變量部分的代碼定義為單獨的方法,然后將這個方法聲明為synchronized即可。

synchronized塊###

我們同樣可以通過使用synchronized關鍵字聲明一個synchronized塊,代碼如下

        synchronized (inner) {
            //需要進行訪問控制的代碼
        }

上述代碼中的inner可以為類,也可以為類的實例,具體實現可以如下所示,此種方式同synchronized進行比較的話,更為靈活,我們可以根據實際的需要對于想要添加鎖的代碼或類加鎖。

<h2>1.示例演示第1條規則,同一時間只能有一個線程在執行同步代碼塊</h2>

public class ThreadTest implements Runnable {
    public void run() {
        //同步代碼塊,該代碼塊在同一時間只能被一個線程調用,其他想要執行同步代碼塊的線程必須要等待當前線程執行完代碼塊之后才能繼續執行同步代碼塊的內容
        synchronized (this) {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + " synchronized index " + i);
            }
        }
    }

    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        Thread ta = new Thread(threadTest,"Thread ta");
        Thread tb = new Thread(threadTest,"Thread tb");
        ta.start();
        tb.start();
    }
}

執行結果為

Thread ta synchronized index 0
Thread ta synchronized index 1
Thread ta synchronized index 2
Thread tb synchronized index 0
Thread tb synchronized index 1
Thread tb synchronized index 2

<h2>2.示例演示第2條規則,同一時間其他線程可以調用對象的其他非同步代碼塊</h2>

public class ThreadTest {
    public void synchron() {
        synchronized (this) {
            int i = 4;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public void noSynchron() {
        int i = 4;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        Thread ta = new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.synchron();
            }
        }, "ThreadTA");
        Thread tb = new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.noSynchron();
            }
        }, "ThreadTB");
        ta.start();
        tb.start();
    }
}

執行結果為

ThreadTA:3
ThreadTB:3
ThreadTA:2
ThreadTB:2
ThreadTA:1
ThreadTB:1
ThreadTB:0
ThreadTA:0

<h2>3.示例演示第3條規則,我們修改上面代碼的noSynchron方法為同步代碼塊</h2>
代碼如下

    public void noSynchron() {
        synchronized (this) {
            int i = 4;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
        }
    }

執行結果為

ThreadTA:3
ThreadTA:2
ThreadTA:1
ThreadTA:0
ThreadTB:3
ThreadTB:2
ThreadTB:1
ThreadTB:0

<h2>4.示例演示第4條規則,我們修改上面代碼的noSynchron方法為如下代碼</h2>

    public synchronized void noSynchron() {
        int i = 4;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

執行結果與上面相同。
<h2>5.示例演示第5條規則,以上規則對于其他對象鎖同樣適用</h2>

public class ThreadTest {
    class innerClass {
        private void method1() {
            int i = 4;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " innerClass.method1 " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
        }
        private void method2() {
            int i = 4;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " innerClass.method2 " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public void getInnerClass1(innerClass inner) {
        synchronized (inner) {
            inner.method1();
        }
    }

    public void getInnerClass2(innerClass inner) {
        inner.method2();
    }

    public static void main(String[] args) {
        final ThreadTest threadTest = new ThreadTest();
        final innerClass inner = threadTest.new innerClass();
        Thread ta = new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.getInnerClass1(inner);
            }
        }, "ThreadTA");
        Thread tb = new Thread(new Runnable() {
            @Override
            public void run() {
                threadTest.getInnerClass2(inner);
            }
        }, "ThreadTB");
        ta.start();
        tb.start();
    }
}

執行結果

ThreadTA innerClass.method1 3
ThreadTB innerClass.method2 3
ThreadTA innerClass.method1 2
ThreadTB innerClass.method2 2
ThreadTA innerClass.method1 1
ThreadTB innerClass.method2 1
ThreadTA innerClass.method1 0
ThreadTB innerClass.method2 0

那么,如果我們在method2方法上增加同步方法鎖,則方法變為

        private synchronized void method2() {
            int i = 4;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " innerClass.method2 " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            }
        }

執行結果變為

ThreadTA innerClass.method1 3
ThreadTA innerClass.method1 2
ThreadTA innerClass.method1 1
ThreadTA innerClass.method1 0
ThreadTB innerClass.method2 3
ThreadTB innerClass.method2 2
ThreadTB innerClass.method2 1
ThreadTB innerClass.method2 0

至此可以看出,盡管兩個線程訪問了內部類中不同的兩個方法,但是由于都是使用了同步代碼塊synchronized關鍵字進行了標識,因此在調用中,由于ThreadTA先獲得了innerClass類的對象鎖,導致在ThreadTA執行完代碼塊之前,ThreadTB被阻塞,直到ThreadTA執行完代碼塊后,ThreadTB才繼續執行。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容