文章出處

首先得明白什么是線程安全:
線程安全編程中的術語,指某個函數 (計算機科學)函數庫多線程環境中被調用時,能夠正確地處理各個線程局部變量,使程序功能正確完成。
這是維基百科里的資料,看完后還不是特別的明白。我自己的理解就是在多線程環境下,某塊代碼中訪問的資源不會發生沖突。
 
 
寫這篇筆記的起因是上周的支付寶電話面試中問了我一個線程安全的問題,就是有一個類,他的方法A是加了synchronized關鍵字的,然后分別創建這個類的兩個實例,請問,當多個線程同時訪問這兩個實例中的方法A時synchronized會起作用嗎?
 
當時我的回答還是很明確而自信的說“會”,今天覺得這個問題要好好研究一下,于是就寫了代碼做了一下測試,發現自己答錯了,這或許是我面試失敗的原因之一吧。代碼貼出來:
class Thread2 extends Thread{
    public void run() {
        MyObj obj = new MyObj();
        try {
            obj.sayHello(3000);//3秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Thread3 extends Thread{
    public void run() {
        MyObj obj = new MyObj();
        try {
            obj.sayHello(1000);//1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyObj {
public synchronized void sayHello(int delay) throws InterruptedException{ Thread.sleep(delay); System.out.println("delay" + delay); } } public class ThreadTest { public static void main(String[] args) throws Exception{ Thread2 t2 = new Thread2(); Thread3 t3 = new Thread3(); t2.start();//先讓t2線程啟動,因為t2要等待3秒,如果線程同步有作用的話t3會處理阻塞狀態 t3.start(); } }

代碼中的MyObj類就是我用于測試的線程安全的對象,它包含了一個sayHello方法,他是帶有synchronized關鍵字的。但測試結果卻是

delay1000
delay3000

這說明關鍵字沒有起作用,也說明了不同的對象實例synchronized方法時是線程不安全的。知道這個結果心里有些難過,只不過事情已經過去就當是學習了經驗吧。

 

synchronized除了修飾方法外,還可以修飾代碼塊,那就試試看吧,寫了一個新的類:

class MyObj2 {
    private static Object lockObj = new Object();
    public void sayHello(int delay) throws InterruptedException{        
        synchronized(lockObj){
            Thread.sleep(delay);
            System.out.println("delay" + delay);
        }
    }
}

用這個類做測試的結果:

delay3000
delay1000

這說明synchronized修改代碼塊時線程同步是起作用的,但這里要注意,采用synchronized代碼塊時,synchronized(lockObj)中的lockObj對象是一個靜態對象,所以他們對應的鎖是同一個,這就可以實現線程間的同步。如果換成synchronized(this)又無法同步。

于是就得想明白為什么會有這兩種差別呢?原來JAVA中每個對象都對應一個鎖,synchronized關鍵字是通過檢查這個對象鎖的狀態來調度的。這下就明白了,原來關鍵就在于對象對應的那個鎖,MyObj之所以不能同步是因為創建了兩不同的對象實例,自然對應的對象鎖就不同,而synchronized修飾的是方法時,其對應檢查的是當前對象的鎖,所以就會出現不同步的情況。

 

后來在網上查資料的同時也發現一個叫類鎖的東東,通過類鎖獲得的類本身,是唯一的,那么就應該是可以同步了,代碼如下:

class MyObj3 {
    public synchronized static void sayHello(int delay) throws InterruptedException{
        Thread.sleep(delay);
        System.out.println("delay" + delay);
    }
}

得到的結果

delay3000
delay1000

這說明已經同步了,synchronized+static一起修改時獲得的是類鎖,獲得類本身,所以只有一個,那么同步自然有了效果。

 

注:“光之子”在評論中指出了本文的一些問題,有興趣的朋友看到本文請把評論也看看吧。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()