一張圖講解物件鎖和關鍵字synchronized修飾方法(程式碼塊)

阿飛雲發表於2019-04-19

做一個積極的人

編碼、改bug、提升自己

我有一個樂園,面向程式設計,春暖花開!

每個物件在出生的時候就有一把鑰匙(監視器Monitor),那麼被synchronized 修飾的方法相當於給方法加了一個鎖,這個方法就可以進行同步,在多執行緒的時候,不會出現執行緒安全問題。

注:Monitor是 Java中用以實現執行緒之間的互斥與協作的主要手段,它可以看成是物件或者 Class的鎖。每一個物件都有,也僅有一個 Monitor。

下面通過一張圖片進行講解:

一、請看圖片

圖片看不清,請點選這裡 : 高清大圖

這裡寫圖片描述

二、圖片對應的程式碼

import java.util.Date;

/**
 * 測試的object類
 *
 * @author:dufy
 * @version:1.0.0
 * @date 2017/9/29
 * @email 742981086@qq.com
 */
public class ObjectTest {


    public synchronized  void methodA(){
        try {
            System.out.println("This is methodA ...." + Thread.currentThread().getName() + ": " + new Date());
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void methodB(){

        System.out.println("This is methodB ...." + Thread.currentThread().getName() + ": " + new Date());
    }

    public synchronized void methodC(){

        try {
            System.out.println("This is methodC ...." + Thread.currentThread().getName() + ": " + new Date());
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


}

複製程式碼
package com.dufy.concurrentcode;

/**
 * 測試執行緒類
 *
 * @author:dufy
 * @version:1.0.0
 * @date 2017/9/29
 * @email 742981086@qq.com
 */
public class ThreadTest extends Thread{

    public static void main(String[] args) {
        ObjectTest ot = new ObjectTest();
        Thread1 t1 = new Thread1(ot,"thread1");
        Thread2 t2 = new Thread2(ot,"thread2");
        Thread3 t3 = new Thread3(ot,"thread3");
        Thread4 t4 = new Thread4(ot,"thread4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }


    static class Thread1 extends Thread{
        private ObjectTest objectTest;

        public Thread1(ObjectTest objectTest,String name){
            setName(name);
            this.objectTest = objectTest;
        }
        @Override
        public void run() {
            super.run();
            objectTest.methodA();
        }
    }

    static class Thread2 extends Thread{
        private ObjectTest objectTest;

        public Thread2(ObjectTest objectTest,String name){
            setName(name);
            this.objectTest = objectTest;
        }
        @Override
        public void run() {
            super.run();
            objectTest.methodB();
        }
    }

    static class Thread3 extends Thread{
        private ObjectTest objectTest;

        public Thread3(ObjectTest objectTest,String name){
            setName(name);
            this.objectTest = objectTest;
        }
        @Override
        public void run() {
            super.run();
            objectTest.methodA();
        }
    }

    static class Thread4 extends Thread{
        private ObjectTest objectTest;

        public Thread4(ObjectTest objectTest,String name){
            setName(name);
            this.objectTest = objectTest;
        }
        @Override
        public void run() {
            super.run();
            objectTest.methodC();
        }
    }
}
複製程式碼

執行結果:

This is methodB ....thread2: Fri Sep 29 23:21:17 CST 2017
This is methodA ....thread1: Fri Sep 29 23:21:17 CST 2017
This is methodC ....thread4: Fri Sep 29 23:21:18 CST 2017
This is methodA ....thread3: Fri Sep 29 23:21:21 CST 2017
複製程式碼

注: 1、執行的結果可能和上圖講的執行緒流程不同,沒有關係,只要理解物件鎖和synchronized的核心思想就好,執行緒的執行本來就是具有隨機性這個特點。 2、此段程式碼是同步方法,其實同步的程式碼塊也是一個道理,同步程式碼塊用synchronized(this)時候,當一個執行緒訪問object的一個synchronized(this)同步程式碼塊的時候,其他執行緒對object中所有其他的synchronized(this)同步的程式碼塊訪問都被阻塞 (阻塞的是同步程式碼塊,執行緒依然可以進入同步程式碼塊的方法)

三、總結說明

  1. 每個物件都有一把鎖(物件監視器),關鍵字synchronized取得鎖都是物件鎖,而不是把一段程式碼或方法(函式)當做鎖。
  2. 上圖所示,哪個執行緒先執行帶有synchronized關鍵字的方法,哪個執行緒就持有這個方法所屬物件的鑰匙。其他執行緒只能處於等待狀態。
  3. 呼叫關鍵字synchronized宣告的方法,一定是排隊執行的。這才是使用synchronized關鍵字的作用,排隊執行,如果有共享資源的話,那麼共享資源的讀取就是執行緒安全的。
  4. 如果一個執行緒持有 object的鑰匙,那麼其他執行緒可以訪問object物件沒有上鎖的方法,也就是非synchronized型別的方法。

四、Monitor和執行緒關係

首先看一下執行緒和 Monitor之間關係,以 及執行緒的狀態轉換圖。通過圖講解一下整個過程。

這裡寫圖片描述

上圖分為三塊:Entry Set(進入區) 、The Owner(擁有區)、Wait Set(等待區)。

  • Entry Set(進入區):表示執行緒通過synchronized要求獲取物件的鎖。如果物件未被鎖住,則迚入擁有者;否則則在進入區等待。一旦物件鎖被其他執行緒釋放,立即參與競爭。

  • The Owner(擁有區):表示某一執行緒成功競爭到物件鎖。

  • Wait Set(等待區):表示執行緒通過物件的wait方法,釋放物件的鎖,並在等待區等待被喚醒。

從圖中可以看出,一個 Monitor在某個時刻,只能被一個執行緒擁有,該執行緒就是 “Active Thread”,而其它執行緒都是 “Waiting Thread”,分別在兩個佇列 “ Entry Set”和 “Wait Set”裡面等候。

上面的內容講解引自 :Java命令學習系列(二)——Jstack 中關於Monitor的講解。

參考文章

Java多執行緒程式設計核心技術

Java命令學習系列(二)——Jstack


謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!



不管做什麼,只要堅持下去就會看到不一樣!在路上,不卑不亢!

願你我在人生的路上能都變成最好的自己,能夠成為一個獨擋一面的人

一張圖講解物件鎖和關鍵字synchronized修飾方法(程式碼塊)

© 每天都在變得更好的阿飛雲

相關文章