Synchronized詳解

什么是Synchronized,有什么用?

Java語言的關(guān)鍵字之剧,當(dāng)它用來修飾一個(gè)方法或者一個(gè)代碼塊的時(shí)候牌芋,能夠保證在同一時(shí)刻最多只有一個(gè)線程執(zhí)行該段代碼迂猴。

使用方式

1. 修飾對(duì)象方法(也就是非靜態(tài)方法)

如果一個(gè)線程正在訪問這個(gè)對(duì)象中synchronized修飾的方法時(shí)佑菩,那么其他試圖訪問該對(duì)象任何synchronized修飾的方法的線程都將被阻塞.

很抽象,沒關(guān)系,繼續(xù)往下.

/**
 * 對(duì)象方法加synchronized關(guān)鍵字
 */
class SyncDemo {
    synchronized void exec1() {
        for (int i = 0; i < 5; i++) {
            Thread.sleep(100);//省略try_catch
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized void exec2() {
        for (int i = 0; i < 5; i++) {
            Thread.sleep(100);//省略try_catch
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

同一個(gè)對(duì)象,同一個(gè)方法

public class SyncDemoTest {
    public static void main(String[] args) {
      //先將對(duì)象實(shí)例化,兩個(gè)線程用的是一個(gè)對(duì)象
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread2").start();
    }
}

運(yùn)行結(jié)果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec1-->thread2-->0
exec1-->thread2-->1
exec1-->thread2-->2
exec1-->thread2-->3
exec1-->thread2-->4

同一個(gè)對(duì)象,不同方法

public class SyncDemoTest {
    public static void main(String[] args) {
      //先將對(duì)象實(shí)例化,兩個(gè)線程用的是一個(gè)對(duì)象
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo1.exec2();
            }
        }, "thread2").start();
    }
}

運(yùn)行結(jié)果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

從結(jié)果中我們會(huì)發(fā)現(xiàn),當(dāng)exec1()執(zhí)行的時(shí)候,exec2()必須要等待exec1()執(zhí)行完成后才會(huì)執(zhí)行.

說明: 如果一個(gè)對(duì)象有多個(gè)synchronized方法扁凛,某一時(shí)刻某個(gè)線程已經(jīng)進(jìn)入到了某個(gè)synchronized方法忍疾,那么在該方法沒有執(zhí)行完畢前,其他線程是無法訪問該對(duì)象的任何synchronized方法的谨朝。

結(jié)論

當(dāng)synchronized關(guān)鍵字修飾一個(gè)方法的時(shí)候卤妒,該方法叫做同步方法甥绿。
Java中的每個(gè)對(duì)象都有一個(gè)鎖(lock),或者叫做監(jiān)視器(monitor)则披,當(dāng)一個(gè)線程訪問某個(gè)對(duì)象的synchronized方法時(shí)共缕,將該對(duì)象上鎖,其他任何線程都無法再去訪問該對(duì)象的synchronized方法了(這里是指所有的同步方法士复,而不僅僅是同一個(gè)方法)图谷,直到之前的那個(gè)線程執(zhí)行方法完畢后(或者是拋出了異常),才將該對(duì)象的鎖釋放掉阱洪,其他線程才有可能再去訪問該對(duì)象的synchronized方法便贵。

注意這時(shí)候是給對(duì)象上鎖,如果是不同的對(duì)象冗荸,則各個(gè)對(duì)象之間沒有限制關(guān)系承璃。

one more thing

代碼中構(gòu)造兩個(gè)線程時(shí),分別在run方法里傳入不同的SyncDemo1對(duì)象,那么兩個(gè)線程的執(zhí)行之間將沒有什么制約關(guān)系蚌本。

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec2();
            }
        }, "thread2").start();
    }

2. 修飾靜態(tài)方法

當(dāng)一個(gè)synchronized關(guān)鍵字修飾的方法同時(shí)又被static修飾盔粹,之前說過,非靜態(tài)的同步方法會(huì)將對(duì)象上鎖程癌,但是靜態(tài)方法不屬于對(duì)象舷嗡,而是屬于類,它會(huì)將這個(gè)方法所在的類的Class對(duì)象上鎖席楚。

一個(gè)類不管生成多少個(gè)對(duì)象咬崔,它們所對(duì)應(yīng)的是同一個(gè)Class對(duì)象。

/**
 * 靜態(tài)方法加synchronized關(guān)鍵字
 */
class SyncDemo {
  //加入靜態(tài)修飾
    synchronized static void exec1() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
    //加入靜態(tài)修飾
    synchronized static void exec2() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

public class SyncDemoTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SyncDemo.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                SyncDemo.exec2();
            }
        }, "thread2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec1();
            }
        }, "thread3").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                new SyncDemo().exec2();
            }
        }, "thread4").start();
    }
}

運(yùn)行結(jié)果
exec1-->thread1-->0
exec1-->thread1-->1
exec1-->thread1-->2
exec1-->thread1-->3
exec1-->thread1-->4
exec2-->thread4-->0
exec2-->thread4-->1

exec2-->thread4-->2
exec2-->thread4-->3
exec2-->thread4-->4
exec1-->thread3-->0
exec1-->thread3-->1
exec1-->thread3-->2
exec1-->thread3-->3
exec1-->thread3-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

從結(jié)果中我們會(huì)發(fā)現(xiàn),4個(gè)線程的調(diào)用時(shí)依次執(zhí)行的

如果某個(gè)synchronized方法是static的烦秩,那么當(dāng)線程訪問該方法時(shí)垮斯,它鎖的并不是synchronized方法所在的對(duì)象,而是synchronized方法所在的類所對(duì)應(yīng)的Class對(duì)象只祠。Java中兜蠕,無論一個(gè)類有多少個(gè)對(duì)象,這些對(duì)象會(huì)對(duì)應(yīng)唯一一個(gè)Class對(duì)象抛寝,因此當(dāng)線程分別訪問同一個(gè)類的兩個(gè)對(duì)象的兩個(gè)static熊杨,synchronized方法時(shí),它們的執(zhí)行順序也是順序的盗舰,也就是說一個(gè)線程先去執(zhí)行方法晶府,執(zhí)行完畢后另一個(gè)線程才開始

3. 修飾代碼塊

如下,

對(duì)象鎖

    void exec() {
        synchronized (this) {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println("exec-->" + Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

其效果等同于修飾對(duì)象方法.this作為對(duì)象鎖.

類鎖

    static void exec() {
        synchronized (SyncDemo.class) {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(100);
                System.out.println("exec-->" + Thread.currentThread().getName() + "-->" + i);
            }
        }
    }

其效果等同于修飾靜態(tài)方法.

4. 混合修飾

class SyncDemo {
    synchronized void exec() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec -->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized static void exec1() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec1-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }

    synchronized static void exec2() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("exec2-->" + Thread.currentThread().getName() + "-->" + i);
        }
    }
}

public class SyncDemoTest {
    public static void main(String[] args) {
        final SyncDemo syncDemo = new SyncDemo();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec();
            }
        }, "thread ").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec1();
            }
        }, "thread1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                syncDemo.exec2();
            }
        }, "thread2").start();
    }
}

運(yùn)行結(jié)果

exec -->thread -->0
exec1-->thread1-->0
exec -->thread -->1
exec1-->thread1-->1
exec -->thread -->2
exec1-->thread1-->2
exec -->thread -->3
exec1-->thread1-->3
exec -->thread -->4
exec1-->thread1-->4
exec2-->thread2-->0
exec2-->thread2-->1
exec2-->thread2-->2
exec2-->thread2-->3
exec2-->thread2-->4

從結(jié)果來看,對(duì)象鎖的thread和類鎖的thread1 兩個(gè)線程交替進(jìn)行.

說明對(duì)象鎖跟類的鎖之間沒有相互制約關(guān)系

Tips

  1. synchronized方法與synchronized代碼塊的區(qū)別

synchronized methods(){} 與synchronized(this){}之間沒有什么區(qū)別,只是 synchronized methods(){} 便于閱讀理解钻趋,而synchronized(this){}可以更精確的控制沖突限制訪問區(qū)域川陆,有時(shí)候表現(xiàn)更高效率

  1. synchronized關(guān)鍵字是不能繼承的

  2. 使用synchronized關(guān)鍵字解決線程的同步問題會(huì)帶來一些執(zhí)行效率上的問題。

JDK 5.0引入了這樣一個(gè)包:java.util.concurrent:

簡(jiǎn)述

  1. 無論synchronized關(guān)鍵字加在方法上還是對(duì)象上蛮位,如果它作用的對(duì)象是非靜態(tài)的较沪,則它取得的鎖是對(duì)象鳞绕;如果synchronized作用的對(duì)象是一個(gè)靜態(tài)方法或一個(gè)類,則它取得的鎖是類.

  2. 每個(gè)對(duì)象只有一個(gè)鎖(lock)與之相關(guān)聯(lián)尸曼,誰拿到這個(gè)鎖誰就可以運(yùn)行它所控制的那段代碼们何。

  3. 類的鎖跟對(duì)象的鎖之間沒有制約關(guān)系.

參考

  1. synchronized關(guān)鍵字詳解
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市控轿,隨后出現(xiàn)的幾起案子冤竹,更是在濱河造成了極大的恐慌,老刑警劉巖解幽,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贴见,死亡現(xiàn)場(chǎng)離奇詭異烘苹,居然都是意外死亡躲株,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門镣衡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霜定,“玉大人,你說我怎么就攤上這事廊鸥⊥疲” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵惰说,是天一觀的道長磨德。 經(jīng)常有香客問我,道長吆视,這世上最難降的妖魔是什么典挑? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮啦吧,結(jié)果婚禮上您觉,老公的妹妹穿的比我還像新娘。我一直安慰自己授滓,他們只是感情好琳水,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著般堆,像睡著了一般在孝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淮摔,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天私沮,我揣著相機(jī)與錄音,去河邊找鬼噩咪。 笑死顾彰,一個(gè)胖子當(dāng)著我的面吹牛极阅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涨享,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼筋搏,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了厕隧?” 一聲冷哼從身側(cè)響起奔脐,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吁讨,沒想到半個(gè)月后髓迎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡建丧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年排龄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翎朱。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橄维,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拴曲,到底是詐尸還是另有隱情争舞,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布澈灼,位于F島的核電站竞川,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叁熔。R本人自食惡果不足惜委乌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望者疤。 院中可真熱鬧福澡,春花似錦、人聲如沸驹马。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糯累。三九已至算利,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泳姐,已是汗流浹背效拭。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缎患。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓慕的,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挤渔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肮街,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • 一、概念 synchronized 是 Java 中的關(guān)鍵字判导,是利用鎖的機(jī)制來實(shí)現(xiàn)同步的嫉父。 鎖機(jī)制有如下兩種特性:...
    zly394閱讀 11,773評(píng)論 1 6
  • 1、了解synchronized synchronized是Java中的關(guān)鍵字眼刃,是一種同步鎖绕辖。當(dāng)多個(gè)并發(fā)線程訪問同...
    黒貓閱讀 1,636評(píng)論 0 2
  • Java8張圖 11、字符串不變性 12擂红、equals()方法仪际、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,709評(píng)論 0 11
  • 在之前的文章java面試 synchronized關(guān)鍵字中篮条,已經(jīng)詳細(xì)的介紹了synchronized關(guān)鍵字的用法和...
    步積閱讀 2,511評(píng)論 1 4
  • 本文主要講了java中多線程的使用方法弟头、線程同步、線程數(shù)據(jù)傳遞涉茧、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等疹娶。 首先講...
    李欣陽閱讀 2,458評(píng)論 1 15