java synchronized


  網上有許多關於synchronized關鍵字用法的文章,發現有些文章里介紹的用法和場景,這里我整理一下,通過實踐,以一種有別於傳統的寫法介紹這個關鍵字的用法!用圖文並茂的方式展示出來,希望大家理解起來更加簡單易懂。本人知識有限,不足或錯誤的地方,歡迎指正,謝謝。

准備個實際測試用的例子

public class TestProgram extends Thread {
    
    static int j = 0;
    
    public static synchronized void testA(){
        j++;
        System.out.println(Thread.currentThread().getName() + ": " + j);
    }
    
    public synchronized void testB(){
        j++;
        System.out.println(Thread.currentThread().getName() + ": " + j);
    }
    
    public void testC(){
        System.out.println("into testC...");
        synchronized (this) {
            j++;
            System.out.println(Thread.currentThread().getName() + ": " + j);
        }
    }
   

    @Override
    public void run() {
        testA();
        testB();
        testC();
    }


這就是這個例子的所有代碼,代碼簡單。這里簡短介紹一下:

  總共有三個測試方法,分別是testA、testB和testC。testA方法是一個加了synchronized關鍵字和static關鍵字的方法,testB是只加了synchronized關鍵字的方法,testC是代碼塊中包含了synchronized塊的方法,這里分別用兩種模擬環境測試這三個方法的不同,用來區分三個方法在不同場景下的不同!

首先是測試環境(兩個線程跑同一個program,我在這里只創建了一個TestProgram實例)

同一實例下測試

public static void main(String[] args) {
    TestProgram program = new TestProgram();
    Thread t = new Thread(program);
    Thread t2 = new Thread(program);
    t.start();
    t2.start();
}

測試testA()

這里我用兩個線程去測試,通過start()調用run()方法,我在testA()方法里打了個斷點,然后兩個線程跑起來,我們看結果

我們在debug模式下,用多線程調試看結果,可以看到生成了Thread-1和Thread-2兩個線程,在紅色標記的代碼中,可以看到Thread-1進入了testA()方法,而Thread-2則沒有進入到testA()方法,這說明synchronized起到了同步作用。

接着我讓線程Thread-1跑完testA()方法后,Thread-2方法就直接被激活了,直接進入testA()方法,同步有效了。

得到第一個結論,在同一個實例下,synchronized關鍵字是對static方法起到同步作用的。

當Thread-1和Thread-2都跑完testA()方法后輸出結果:

Thread-1: 1
Thread-2: 2

為什么這個結論前面要加一個同一個實例?后面會看到,不在同一個實例下的多線程,synchronized關鍵效果不一樣!

接下來是進入testB()方法

當Thread-1進入testB()方法時,看Thread-2線程進入了一種叫stepping的模式(類似於等待),在進入這個等待的過程中,如果我們將Thread-1線程跑完testB()方法,那么Thread-2的狀態將由stepping改成suspended。簡單的說就是當一個線程進入testB()時,其他線程都在等這個線程跑完。

在這里發現synchronized方法也起到了同步的效果。

再看測試testC()方法的效果

 

 我們可以看到,兩個線程都進入到了testC()方法,而且兩個線程的狀態都是suspended。接下來,當線程Thread-1進入到synchronized代碼塊中,看結果

可以看到當線程Thread-1進入代碼塊后,Thread-2再進入這個代碼塊,狀態就變成了stepping等待的情況了。說明這里synchronized方式同步有效。

最后執行testC()方法后的結果是:

into testC...
into testC...
Thread-1: 5
Thread-2: 6

 

以上就是在同一個實例下跑出來的結果。但如果是這種情況下,情況就不同了,這也是我們經常遇到的問題,有時候感覺怎么與以前的理論相駁,接着分析

不同實例下的測試

public static void main(String[] args) {
    TestProgram program = new TestProgram();
    TestProgram program2 = new TestProgram();
    Thread t = new Thread(program);
    Thread t2 = new Thread(program2);
    t.start();
    t2.start();
}

換了兩行代碼,在兩個相同的TestProgram中,創建兩個對象,與上面的寫法差異在兩個線程跑各自的實例,這個時候synchronized的作用效果就不太一樣了

首先是testA()方法

測試的結果是

Thread-3: 1
Thread-2: 2

這個結果和開始測試的結果一樣,說明在這里synchronized起到了同步效果!

測試testB()

這里結果就和上面測試testB()效果就不一樣了,我們看效果

從圖中可以看到,兩個線程都同時進入了testB()方法中,也就是synchronized關鍵字沒起到同步兩個線程的作用

這里得出一個結論:兩個不同的實例,在不同的線程環境下,synchronized並不能實現同步效果,當然,static方法除外

后面的testC()方法也一樣,看看效果

兩個線程都同時進入了第22行代碼,就是同時在synchronized塊中,說明這個synchronized代碼塊沒有起到同步的效果。

 

綜合以上所訴,我得出一個這樣的結論:

如果多線程是對同一個對象的引用,那么synchronized關鍵字在所有的情況下都有同步的效果,如果多線程是對同一個對象的不同引用(創建了多個對象),除了static方法上面加的synchronized方法有同步引用的效果外,其他的synchronized(包括synchronized代碼塊)將沒有同步的效果。

 

至於為什么static方法上加synchronized方法,在不同的場景下都有效,原因是:被static修飾的成員變量和成員方法獨立於該類的任何對象,static方法不屬於類創建的引用,所以無論創建了多少個對象,對於static方法而言,都跟它沒關系。

ps:synchronized(this)這個this就是指對象的引用,這里指我當前線程的對象引用。

我的疑問:為什么創建一個對象調試的時候,是Thread-1和Thread-2,而我創建兩個對象調試的時候卻是Thread-2和Thread-3?

 

 


免責聲明!

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



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