Synchronized三種用法

首先我們了解到Java中的線程同步鎖可以是任意對象搀愧。
這里我們介紹synchronized的三種應用方式:

1.作用于實例方法家淤,當前實例加鎖佳晶,進入同步代碼前要獲得當前實例的鎖抄瓦;

2.作用于靜態(tài)方法坐求,當前類加鎖蚕泽,進去同步代碼前要獲得當前類對象的鎖;

3.作用于代碼塊桥嗤,這需要指定加鎖的對象须妻,對所給的指定對象加鎖,進入同步代碼前要獲得指定對象的鎖泛领。

這三種應用方式接下來分別介紹

synchronized修飾實例方法(普通方法)

??使用時荒吏,作用范圍為整個函數(shù),這里所謂的實例鎖就是調(diào)用該實例方法(不包括靜態(tài)方法)的對象渊鞋。不多BB绰更,上代碼:
【demo1】

public class SyncTest implements Runnable{
    //共享資源變量
    int count = 0;

    @Override
    public synchronized void run() {
        for (int i = 0; i < 5; i++) {
            increaseCount();
            System.out.println(Thread.currentThread().getName()+":"+count++);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest syncTest1 = new SyncTest();
//        SyncTest syncTest2 = new SyncTest();
        Thread thread1 = new Thread(syncTest1,"thread1");
        Thread thread2 = new Thread(syncTest1, "thread2");
        thread1.start();
        thread2.start();
    }
}
 /**
     * 輸出結果
     thread1:0
     thread1:1
     thread1:2
     thread1:3
     thread1:4
     thread2:5
     thread2:6
     thread2:7
     thread2:8
     thread2:9
     */

??代碼中開啟了兩個線程去操作一個變量(共享變量),count++是先讀取值,再寫回一個新值锡宋。我們想一下儡湾,如果第一個線程執(zhí)行這一過程中,第二個線程拿到寫回之前的count值执俩,做count++操作徐钠,那么這就造成了線程不安全。所以這里在run方法加上synchronized役首,獲取一個對象鎖尝丐,代碼中的實例鎖就是syncTest1了。
??同時我們從輸出結果看出:當一個線程正在訪問一個對象synchronized實例方法時宋税,別的線程是訪問不了的摊崭。一個對象一把鎖說的就是這個讼油,當線程獲取了該對象的鎖后杰赛,其他線程無法獲取該對象的鎖,當然就訪問不了該對象的synchronized方法矮台,但是乏屯!但是根时!但是!可以訪問該對象的其他未被synchronized修飾的方法辰晕。
??如果是一個線程 A 需要訪問實例對象 obj1 的 synchronized 方法 f1(當前對象鎖是obj1)蛤迎,另一個線程 B 需要訪問實例對象 obj2 的 synchronized 方法 f2(當前對象鎖是obj2),這樣是允許的含友,因為兩個實例對象鎖并不同相同替裆,此時如果兩個線程操作數(shù)據(jù)并非共享的,線程安全是有保障的窘问,遺憾的是如果兩個線程操作的是共享數(shù)據(jù)辆童,那么線程安全就有可能無法保證了。我們把上面代碼中的main方法中的注釋放開惠赫,表達這一線程不安全的現(xiàn)象
【demo2】

public class SyncTest implements Runnable{
    //共享資源變量
    int count = 0;

    @Override
    public synchronized void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+count++);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest syncTest1 = new SyncTest();
        SyncTest syncTest2 = new SyncTest();
        Thread thread1 = new Thread(syncTest1,"thread1");
        Thread thread2 = new Thread(syncTest2, "thread2");
        thread1.start();
        thread2.start();
    }
    /**
     * 輸出結果
        thread1:0
        thread2:0
        thread1:1
        thread2:1
        thread1:2
        thread2:2
        thread1:3
        thread2:3
        thread1:4
        thread2:4
     */
}

??我們從輸出結果來看把鉴,兩個線程可能同時拿到共享變量去做count++操作。上述操作中雖然我們的run方法還是使用synchronized修飾儿咱,但是我們new了兩個實例庭砍。這就意味存在了兩個不同的實例鎖,thread1和thread2分別進入了syncTest1和syncTest2的實例鎖混埠,當然保證不了線程安全怠缸。但是我們也有解決方案啦:如果synchronized修飾的是靜態(tài)方法呢?下面我們再介紹修飾靜態(tài)方法钳宪。

synchronized修飾靜態(tài)方法

??我們知道靜態(tài)方法是不屬于當前實例的凯旭,而是屬性類的,那么這個鎖就是類的class對象鎖使套,上述問題引刃而解罐呼,請看代碼:
【demo3】

public class SyncTest implements Runnable {
    //共享資源變量
    static int count = 0;

    @Override
    public synchronized void run() {
        increaseCount();
    }

    private synchronized static void increaseCount() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + count++);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest syncTest1 = new SyncTest();
        SyncTest syncTest2 = new SyncTest();
        Thread thread1 = new Thread(syncTest1, "thread1");
        Thread thread2 = new Thread(syncTest2, "thread2");
        thread1.start();
        thread2.start();
    }
    /**
     * 輸出結果
     thread1:0
     thread1:1
     thread1:2
     thread1:3
     thread1:4
     thread2:5
     thread2:6
     thread2:7
     thread2:8
     thread2:9
     */
}

??瞧瞧輸出結果,問題解決了沒侦高?同樣是new了兩個不同實例嫉柴,卻保持了線程同步。那是我們synchronizd修飾的是靜態(tài)方法奉呛,run方法中調(diào)用這個靜態(tài)方法计螺,再說一次 靜態(tài)方法不屬于當前實例,而是屬于類瞧壮。所以這個方案其實是用的一個把鎖登馒,而這個鎖就是這個類的class對象鎖。
??需要注意的是如果一個線程A調(diào)用一個實例對象的非static synchronized方法咆槽,而線程B需要調(diào)用這個實例對象所屬類的靜態(tài) synchronized方法陈轿,是允許的,不會發(fā)生互斥現(xiàn)象,因為訪問靜態(tài) synchronized 方法占用的鎖是當前類的class對象麦射,而訪問非靜態(tài) synchronized 方法占用的鎖是當前實例對象鎖(結合demo2蛾娶,demo3)。

synchronized修飾代碼塊

??首先這個使用時的場景是:在某些情況下潜秋,我們編寫的方法體可能比較大蛔琅,同時存在一些比較耗時的操作,而需要同步的代碼又只有一小部分峻呛,如果直接對整個方法進行同步操作罗售,可能會得不償失,此時我們可以使用同步代碼塊的方式對需要同步的代碼進行包裹钩述,這樣就無需對整個方法進行同步操作了莽囤。所以他的作用范圍為synchronizd(obj){}的這個大括號中
【demo4】

public class SyncTest implements Runnable {
    //共享資源變量
    static int count = 0;
    private byte[] mBytes = new byte[0];

    @Override
    public synchronized void run() {
        increaseCount();
    }

    private void increaseCount() {
        //假設省略了其他操作的代碼。
        //……………………
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + count++);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SyncTest syncTest1 = new SyncTest();
        SyncTest syncTest2 = new SyncTest();
        Thread thread1 = new Thread(syncTest1, "thread1");
        Thread thread2 = new Thread(syncTest2, "thread2");
        thread1.start();
        thread2.start();
    }
    /**
     * 輸出結果
     thread1:0
     thread2:0
     thread1:1
     thread2:2
     thread2:4
     thread1:3
     thread2:5
     thread1:5
     thread2:7
     thread1:6
     */
}

??從輸出結果看出切距,這個demo并沒有保證線程安全朽缎,因為我們指定鎖為this,指的就是調(diào)用這個方法的實例對象谜悟。這里我們new了兩個不同的實例對象syncTest1话肖,syncTest2,所以有兩個鎖葡幸,thread1與thread2分別進入自己傳入的對象鎖的線程執(zhí)行increaseCount方法最筒,做成線程不安全。如果把demo4的成員變量注釋放開蔚叨,并將mBytes傳入synchronized后面的括號中床蜘,也是線程不安全的結果。這里之所以加上mBytes這個對象是為了說明synchronized后面的括號中是可以指定任意對象充當鎖的蔑水,而零長度的byte數(shù)組對象創(chuàng)建起來將比任何對象都經(jīng)濟邢锯。當然,如果要使用這個經(jīng)濟實惠的鎖并保證線程安全搀别,那就不能new出多個不同實例對象出來啦丹擎。如果你非要想new兩個不同對象出來,又想保證線程同步的話歇父,那么synchronized后面的括號中可以填入SyncTest.class蒂培,表示這個類對象作為鎖,自然就能保證線程同步啦榜苫。使用方法為:

synchronized(xxxx.class){
  //todo
}

總結

  1. 修飾普通方法 一個對象中的加鎖方法只允許一個線程訪問护戳。但要注意這種情況下鎖的是訪問該方法的實例對象, 如果多個線程不同對象訪問該方法垂睬,則無法保證同步媳荒。

  2. 修飾靜態(tài)方法 由于靜態(tài)方法是類方法抗悍, 所以這種情況下鎖的是包含這個方法的類,也就是類對象肺樟;這樣如果多個線程不同對象訪問該靜態(tài)方法,也是可以保證同步的逻淌。

  3. 修飾代碼塊 其中普通代碼塊 如Synchronized(obj) 這里的obj 可以為類中的一個屬性么伯、也可以是當前的對象,它的同步效果和修飾普通方法一樣卡儒;Synchronized方法 (obj.class)靜態(tài)代碼塊它的同步效果和修飾靜態(tài)方法類似田柔。

參考資料:
http://blog.csdn.net/javazejian/article/details/72828483
http://blog.csdn.net/luoweifu/article/details/46613015

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市骨望,隨后出現(xiàn)的幾起案子硬爆,更是在濱河造成了極大的恐慌,老刑警劉巖擎鸠,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缀磕,死亡現(xiàn)場離奇詭異,居然都是意外死亡劣光,警方通過查閱死者的電腦和手機袜蚕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绢涡,“玉大人牲剃,你說我怎么就攤上這事⌒劭桑” “怎么了凿傅?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長数苫。 經(jīng)常有香客問我聪舒,道長,這世上最難降的妖魔是什么虐急? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任过椎,我火速辦了婚禮,結果婚禮上戏仓,老公的妹妹穿的比我還像新娘疚宇。我一直安慰自己,他們只是感情好赏殃,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布敷待。 她就那樣靜靜地躺著,像睡著了一般仁热。 火紅的嫁衣襯著肌膚如雪榜揖。 梳的紋絲不亂的頭發(fā)上勾哩,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音举哟,去河邊找鬼思劳。 笑死,一個胖子當著我的面吹牛妨猩,可吹牛的內(nèi)容都是我干的潜叛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼壶硅,長吁一口氣:“原來是場噩夢啊……” “哼威兜!你這毒婦竟也來了?” 一聲冷哼從身側響起庐椒,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤椒舵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后约谈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笔宿,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年棱诱,在試婚紗的時候發(fā)現(xiàn)自己被綠了措伐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡军俊,死狀恐怖侥加,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粪躬,我是刑警寧澤担败,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站镰官,受9級特大地震影響提前,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泳唠,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一狈网、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笨腥,春花似錦拓哺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谆级,卻和暖如春烤礁,著一層夾襖步出監(jiān)牢的瞬間讼积,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工脚仔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留勤众,地道東北人。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓鲤脏,卻偏偏與公主長得像们颜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凑兰,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 整理來自互聯(lián)網(wǎng) 1掌桩,JDK:Java Development Kit边锁,java的開發(fā)和運行環(huán)境姑食,java的開發(fā)工具...
    Ncompass閱讀 1,537評論 0 6
  • 一:java概述: 1,JDK:Java Development Kit茅坛,java的開發(fā)和運行環(huán)境音半,java的開發(fā)...
    慕容小偉閱讀 1,774評論 0 10
  • 一:java概述:1,JDK:Java Development Kit贡蓖,java的開發(fā)和運行環(huán)境曹鸠,java的開發(fā)工...
    ZaneInTheSun閱讀 2,635評論 0 11
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法斥铺,內(nèi)部類的語法彻桃,繼承相關的語法,異常的語法晾蜘,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 今年過年邻眷,我妹妹又隨妹夫去了他們家老宅。據(jù)我妹夫說他對老宅其實沒有太多記憶剔交,因為當年他出生后不久全家就從村子里搬到...
    酒紅甜言閱讀 297評論 1 1