java synchronized詳解

在java多線程并發(fā)編程中對于 sychronized 大家一定不陌生,同步關鍵字 synchronized 是java語言中最為常用的同福方法之一伴鳖。那什么情況下才用到 sychronized 呢鸿市?怎么用呢

當有多個線程同時對一個變量進行讀寫的時候就可能出現(xiàn)數(shù)據(jù)不一致的現(xiàn)象锯梁,這時候就需要同步,限制為排隊一個個來焰情。就好比你去買水果陌凳,稱水果的時候如果與別人共用以個袋子,你自己到底買了多少水果你根本不會知道内舟。這時候就是 sychronized 上場的時候了合敦。簡單說 sychronized 就是為了保證數(shù)據(jù)安全的。

sychronized 一般用來修飾一個方法或者一個代碼塊验游。利用sychronized實現(xiàn)同步的基礎:java中每一個對象都可以作為鎖充岛。具體表現(xiàn)為以下3中形式。

1.對于普通方法的同步耕蝉,鎖是當前實例對象崔梗。

2.對于靜態(tài)方法的同步,鎖是當前類的Class對象赔硫。

3.對于同步方法塊炒俱,鎖是sychronized括號里配置的對象。

下面用代碼來演示一遍爪膊。

一权悟、用sychronized修飾一個方法的時候

1.sychronized修飾普通方法
public class ThreadTest extends Thread{
    
    private Test test;
    
    public ThreadTest(String name,Test test) {
        super(name);
        this.test = test;
    }
    
    @Override
    public void run() {
        test.method();
    }
    
}

public class Test{
    
    public static void main(String[] args) {
        Test test1 = new Test();
        Test test2 = new Test();
        ThreadTest thread1 = new ThreadTest("線程1", test1);
        ThreadTest thread2 = new ThreadTest("線程2", test1);  //(1)
    //  ThreadTest thread2 = new ThreadTest("線程2", test2);  //(2)
        thread1.start();
        thread2.start();
    }
    
    public synchronized void method(){
        System.out.println(Thread.currentThread().getName()+" 獲得鎖");
        try {
            System.out.println("開始方法......");
            Thread.sleep(2000); //模擬做其他事情
            System.out.println("結束方法......");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    
    
}

上面程序執(zhí)行的結果是:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
當把代碼(1)注釋掉,打開代碼(2)推盛,執(zhí)行結果是:
線程1 獲得鎖
線程2 獲得鎖
開始方法......
開始方法......
結束方法......
結束方法......

結果分析:
從結果(1)可以看出峦阁,是同一實例對象的時候,多線程同一時刻只能有一個線程能獲得鎖耘成,其他線程必須等鎖釋放了才能獲取榔昔。
從結果(2)可以看出,當不是同一個實例對象的時候瘪菌,線程獲得的鎖與其他線程無關撒会,不需等待其他線程的鎖釋放。
綜合得出:sychronized修飾普通方法的時候师妙,鎖是當前實例對象诵肛,每個實例對象一把鎖,同一時刻只能一個線程獲得鎖默穴。不同實例對象獲取的鎖不一樣怔檩,不需等待其他實例對象鎖的釋放褪秀。

2.sychronized修飾靜態(tài)方法
public synchronized static void method(){
        System.out.println(Thread.currentThread().getName()+" 獲得鎖");
        try {
            System.out.println("開始方法......");
            Thread.sleep(2000); //模擬做其他事情
            System.out.println("結束方法......");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

當把method()改為靜態(tài)的時候。按照上面的執(zhí)行方法薛训,執(zhí)行結果都是
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
從結果可以得出:sychronized修飾靜態(tài)方法的時候媒吗,鎖是當前類的Class對象,每個類對應一把鎖乙埃,多線程不同實例對象同一時刻也只能是一個獲得鎖闸英,其他必須等待鎖的釋放。

二膊爪、用sychronized修飾代碼塊的時候

1.sychronized(object)括號配置的是普通變量的時候
public class Test{
    
    private Object object = new Object();
    
    public static void main(String[] args) {
        Test test1 = new Test();
        Test test2 = new Test();
        ThreadTest thread1 = new ThreadTest("線程1", test1);
        ThreadTest thread2 = new ThreadTest("線程2", test1);  //(1)
    //  ThreadTest thread2 = new ThreadTest("線程2", test2);  //(2)
        thread1.start();
        thread2.start();
    }
    
    public  void method(){
        synchronized (object) {
            System.out.println(Thread.currentThread().getName()+" 獲得鎖");
            try {
                System.out.println("開始方法......");
                Thread.sleep(2000); //模擬做其他事情
                System.out.println("結束方法......");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    
}

上面程序執(zhí)行的結果是:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
當把代碼(1)注釋掉自阱,打開代碼(2)嚎莉,執(zhí)行結果是:
線程1 獲得鎖
線程2 獲得鎖
開始方法......
開始方法......
結束方法......
結束方法......

結果分析:
從結果(1)可以看出米酬,是同一實例對象的時候(變量object一樣),多線程同一時刻只能有一個線程能獲得鎖趋箩,其他線程必須等鎖釋放了才能獲取赃额。
從結果(2)可以看出,當不是同一個實例對象的時候(變量object不一樣)叫确,線程獲得的鎖與其他線程無關跳芳,不需等待其他線程的鎖釋放。
綜合得出:sychronized(object){} object為普通變量的時候竹勉,每個實例對象一把鎖飞盆,同一時刻只能一個線程獲得鎖。不同實例對象獲取的鎖不一樣次乓,不需等待其他實例對象鎖的釋放吓歇。

2.sychronized(object)括號配置的是靜態(tài)變量的時候
private static Object object = new Object();

當把object變量改為靜態(tài)的時候(所有實例對象的object都一樣),執(zhí)行結果都是如下:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
結果分析:sychronized(object){} object為靜態(tài)變量的時候票腰,鎖是當前類的Class對象城看,每個類對應一把鎖,多線程不同實例對象同一時刻也只能是一個獲得鎖杏慰,其他必須等待鎖的釋放测柠。

3.sychronized(object)括號配置的是類Class的時候
public  void method(){
        synchronized (Test.class) {
            System.out.println(Thread.currentThread().getName()+" 獲得鎖");
            try {
                System.out.println("開始方法......");
                Thread.sleep(2000); //模擬做其他事情
                System.out.println("結束方法......");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

當sychronized(object)括號配置的是類Class的時候 執(zhí)行結果都是如下:
線程1 獲得鎖
開始方法......
結束方法......
線程2 獲得鎖
開始方法......
結束方法......
結果分析:sychronized(object){} object為類Class的時候,鎖是當前類的Class對象缘滥,每個類對應一把鎖轰胁,多線程不同實例對象同一時刻也只能是一個獲得鎖,其他必須等待鎖的釋放朝扼。

總結:

A赃阀、A. 無論synchronized關鍵字是修飾在方法上還是代碼塊,如果它作用的對象是非靜態(tài)的吟税,則它取得的鎖是對象凹耙,每個實例對象一把鎖姿现;如果synchronized作用的對象是一個靜態(tài)方法或一個類,則它取得的鎖是對類肖抱,該類所有的對象同一把鎖备典。
B. 每個對象只有一個鎖(lock)與之相關聯(lián),誰拿到這個鎖誰就可以運行它所控制的那段代碼意述。

在用synchronized修飾方法時要注意以下幾點:

  1. synchronized關鍵字不能繼承提佣。

雖然可以使用synchronized來定義方法,但synchronized并不屬于方法定義的一部分荤崇,因此拌屏,synchronized關鍵字不能被繼承。如果在父類中的某個方法使用了synchronized關鍵字术荤,而在子類中覆蓋了這個方法倚喂,在子類中的這個方法默認情況下并不是同步的,而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以瓣戚。當然端圈,還可以在子類方法中調用父類中相應的方法,這樣雖然子類中的方法不是同步的子库,但子類調用了父類的同步方法舱权,因此,子類的方法也就相當于同步了仑嗅。這兩種方式的例子代碼如下:

在子類方法中加上synchronized關鍵字

class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}

在子類方法中調用父類的同步方法

class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
} 

在定義接口方法時不能使用synchronized關鍵字宴倍。
構造方法不能使用synchronized關鍵字,但可以使用synchronized代碼塊來進行同步仓技。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末鸵贬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子浑彰,更是在濱河造成了極大的恐慌恭理,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郭变,死亡現(xiàn)場離奇詭異颜价,居然都是意外死亡,警方通過查閱死者的電腦和手機诉濒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門周伦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人未荒,你說我怎么就攤上這事专挪。” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵寨腔,是天一觀的道長速侈。 經常有香客問我,道長迫卢,這世上最難降的妖魔是什么倚搬? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乾蛤,結果婚禮上每界,老公的妹妹穿的比我還像新娘。我一直安慰自己家卖,他們只是感情好眨层,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著上荡,像睡著了一般趴樱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榛臼,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天伊佃,我揣著相機與錄音,去河邊找鬼沛善。 笑死,一個胖子當著我的面吹牛塞祈,可吹牛的內容都是我干的金刁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼议薪,長吁一口氣:“原來是場噩夢啊……” “哼尤蛮!你這毒婦竟也來了?” 一聲冷哼從身側響起斯议,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤产捞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后哼御,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坯临,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年恋昼,在試婚紗的時候發(fā)現(xiàn)自己被綠了看靠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡液肌,死狀恐怖挟炬,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤谤祖,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布婿滓,位于F島的核電站,受9級特大地震影響粥喜,放射性物質發(fā)生泄漏空幻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一容客、第九天 我趴在偏房一處隱蔽的房頂上張望秕铛。 院中可真熱鬧,春花似錦缩挑、人聲如沸但两。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谨湘。三九已至,卻和暖如春芥丧,著一層夾襖步出監(jiān)牢的瞬間紧阔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工续担, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留擅耽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓物遇,卻偏偏與公主長得像乖仇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子询兴,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內容

  • Java8張圖 11乃沙、字符串不變性 12、equals()方法诗舰、hashCode()方法的區(qū)別 13警儒、...
    Miley_MOJIE閱讀 3,707評論 0 11
  • 由于同一進程的多個線程共享同一片存儲空間,在帶來方便的同時眶根,也帶來了訪問沖突這個嚴重的問題蜀铲。Java語言提供了專門...
    DanieX閱讀 16,368評論 7 16
  • 本文主要講了java中多線程的使用方法、線程同步汛闸、線程數(shù)據(jù)傳遞蝙茶、線程狀態(tài)及相應的一些線程函數(shù)用法、概述等诸老。 首先講...
    李欣陽閱讀 2,454評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,957評論 1 18
  • 1. Java基礎部分 基礎部分的順序:基本語法隆夯,類相關的語法钳恕,內部類的語法,繼承相關的語法蹄衷,異常的語法忧额,線程的語...
    子非魚_t_閱讀 31,631評論 18 399