讓你不再害怕JAVA的鎖(一)

java中的同步鎖包括對象鎖和類鎖锐锣。

對象鎖: 針對的是具體的對象實(shí)例腌闯;類鎖:針對的是整個(gè)class類

現(xiàn)在先讓我們看一些文縐縐的書本概念,暫時(shí)看不懂沒關(guān)系雕憔,只要各位看官耐心看完這篇文章的姿骏,就都明白了

  • 多線程之間的同步實(shí)際上是靠鎖來實(shí)現(xiàn)的
  • 在java的運(yùn)行時(shí)環(huán)境中(即JVM虛擬機(jī)中),會被多線程共享的數(shù)據(jù)包括 堆中的實(shí)例變量方法區(qū)中的類變量 (可以關(guān)注我的簡書號斤彼,后續(xù)會有相關(guān)白話文章介紹JVM內(nèi)存管理)
    在JVM中分瘦,每個(gè)對象和類在邏輯上都與一個(gè)監(jiān)視器相關(guān)聯(lián),這是JVM內(nèi)部實(shí)現(xiàn)的琉苇,我們不需要關(guān)心嘲玫。我們只需要記住以下兩句話
    1. 對于對象,監(jiān)視器保護(hù)的是對象的 實(shí)例變量(對象鎖)
    2. 對于類并扇,監(jiān)視器保護(hù)的是類的 類變量(類鎖)
    這里多說一句去团,實(shí)際上類鎖也是根據(jù)對象鎖來實(shí)現(xiàn)的,具體不再展開介紹穷蛹,以后會專門寫文章介紹土陪。

對于類鎖,JVM在load class文件時(shí)俩莽,會創(chuàng)建一個(gè)與該class相關(guān)聯(lián)的java.lang.Class類的實(shí)例,當(dāng)鎖住這個(gè)class類關(guān)聯(lián)的java.lang.Class實(shí)例的時(shí)候乔遮,實(shí)際上就鎖住了這個(gè)類的class類

對于每一個(gè)對象一個(gè)線程可以多次對同一個(gè)對象上鎖扮超,JVM維護(hù)一個(gè)加鎖計(jì)數(shù)器,線程每獲得一次該對象,計(jì)數(shù)器就加1出刷,每釋放一次璧疗,計(jì)數(shù)器就減 1,當(dāng)計(jì)數(shù)器值為0時(shí)馁龟,鎖就被完全釋放了

我們無需去手動(dòng)進(jìn)行加鎖 釋放鎖崩侠,這些工作JVM都替我們完成了

看到這里,大家應(yīng)該能明白坷檩,其實(shí)我們thread線程中每訪問一個(gè)實(shí)例却音,內(nèi)部其實(shí)都有加鎖 釋放鎖的動(dòng)作。
大家或許有一個(gè)疑問矢炼,那我的變量或代碼(即所謂的臨界區(qū))是怎么實(shí)現(xiàn)多線程安全訪問呢系瓢? 也就是說現(xiàn)在各個(gè)線程都拿到鎖了,但是怎么去讓這個(gè)鎖生效 呢句灌?接下來就是我們的主角synchronized上場了

synchronized關(guān)鍵字

通過上述的分析夷陋,我們知道(線程)鎖上鎖的對象了,那么怎么上鎖呢胰锌,實(shí)際就是通過這個(gè)synchronized關(guān)鍵字進(jìn)行上鎖骗绕。

synchronized 之所以可以實(shí)現(xiàn)同步还蹲,是因?yàn)閖ava中的每一個(gè)對象都會作為鎖琴昆,即java中的每一個(gè)對象JVM在內(nèi)部都為其關(guān)聯(lián)了一個(gè)監(jiān)控器(即加了鎖)

synchronized的作用域包括以下三個(gè)

  • 普通同步方法,鎖針對的是當(dāng)前實(shí)例對象
  • 靜態(tài)同步方法隙畜,鎖針對的是當(dāng)前class對象
  • 同步方法塊榛搔,鎖的是括號里面的對象(synchrozed(this))

如果小伙伴們還是覺得不太好理解诺凡,下面我們通過幾個(gè)測試?yán)觼砜匆幌?/p>

package ke.com;

/**
 * Created by zl on 2019/4/30.
 */
public class ShareResource implements Runnable {

    static int i = 0;

    synchronized public void increase() {
        i = i+1;
    }

    @Override
    public void run() {
        for (int k=0; k<5000;k++) {
            increase();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        ShareResource instance1 = new ShareResource();
        ShareResource instance2 = new ShareResource();

        Thread thread1 = new Thread(instance1);
        Thread thread2 = new Thread(instance2);

        thread1.start();
        thread2.start();
           // join的含義是等兩個(gè)子線程都執(zhí)行完畢后,主線程在執(zhí)行打印代碼
        thread1.join();
        thread2.join();
        System.out.println("i=" + i);
    }
 }

上面的代碼執(zhí)行結(jié)果是下圖所示:


image.png

不是我們想象的10000践惑。
這是因?yàn)槲覀兊膍ain方法中生成了兩個(gè)ShareResource實(shí)例腹泌,即我們獲取的是兩把鎖。所以我們應(yīng)該用類鎖尔觉,而不應(yīng)該用對象鎖(即我們所在了方法上)凉袱,而對象鎖是針對某一個(gè)具體實(shí)例的

我們把測試代碼改成如下,對靜態(tài)方法的synchronized就是對類的synchronized侦铜,這樣我們獲取瑣時(shí)专甩,獲取的就是類鎖

// 只需要把increate方法改成靜態(tài)的
synchronized public static void increase() {
        i = i+1;
}

關(guān)于對象鎖,這里多說一句钉稍,假如 有synchronized method1 和synchronized method2 涤躲,同步了多個(gè)方法,method1的鎖 不會造成 method2訪問線程的阻塞贡未,即兩者的鎖不會相互影響种樱。
另外蒙袍,同步也不會被繼承

說完了普通方法同步和靜態(tài)方法的同步,我們來看一下同步方法塊

同步方法塊

同步代碼塊又分為 synchronized(this)和synchronized(非this)嫩挤,下面我們通過具體代碼來看一下兩者的區(qū)別

package ke.com;

/**
 * Created by zl on 2019/4/30.
 */
public class SynchronizedThisTest {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.start();

        ThreadB b = new ThreadB(service);
        b.start();

    }

}

class Service {
    public void serviceA() {
        synchronized (this) {
            System.out.println("業(yè)務(wù)A開始時(shí)間="+System.currentTimeMillis());
            // 讓調(diào)用線程休眠2s讓出cpu
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("業(yè)務(wù)A結(jié)束時(shí)間="+System.currentTimeMillis());
        }
    }

    public void serviceB() {
        synchronized (this) {
            System.out.println("業(yè)務(wù)B的開始時(shí)間="+System.currentTimeMillis());
            System.out.println("業(yè)務(wù)B的結(jié)束時(shí)間="+System.currentTimeMillis());
        }
    }
}

class ThreadA extends Thread {
    private Service service;
    ThreadA(Service s) {
        super();
        this.service = s;
    }

    @Override
    public void run() {
        super.run();
        // 調(diào)用業(yè)務(wù)A
        service.serviceA();
    }
}

class ThreadB extends Thread {
    private Service service;

    ThreadB(Service s) {
        this.service = s;
    }

    @Override
    public void run() {
        super.run();
        service.serviceB();
    }
}

上面代碼的運(yùn)行結(jié)果是
image.png

可見就算業(yè)務(wù)A的線程的時(shí)間很長害幅,B線程也不會獲取執(zhí)行機(jī)會,說明synchronized(this)是鎖的整個(gè)對象自身

下面我們換成synchronized(object)的形式看看
我們把Service類重寫

class Service {
    public void serviceA() {
        Object o = new Object();
        synchronized (o) {
            System.out.println("業(yè)務(wù)A開始時(shí)間="+System.currentTimeMillis());
            // 讓調(diào)用線程休眠2s讓出cpu
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("業(yè)務(wù)A結(jié)束時(shí)間="+System.currentTimeMillis());
        }
    }

    public void serviceB() {
        Object o = new Object();
        synchronized (o) {
            System.out.println("業(yè)務(wù)B的開始時(shí)間="+System.currentTimeMillis());
            System.out.println("業(yè)務(wù)B的結(jié)束時(shí)間="+System.currentTimeMillis());
        }
    }
}

運(yùn)行結(jié)果如下
image.png

所以在實(shí)際應(yīng)用中一般不用synchronized(this) 而是用一個(gè)synchronized(object)產(chǎn)生兩個(gè)鎖岂昭,來提高執(zhí)行效率

Object中的wait(),notify(),notifyAll()方法一般與synchronized同步鎖綁定使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末以现,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子约啊,更是在濱河造成了極大的恐慌邑遏,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍苹,死亡現(xiàn)場離奇詭異无宿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)枢里,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門孽鸡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人栏豺,你說我怎么就攤上這事彬碱。” “怎么了奥洼?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵巷疼,是天一觀的道長。 經(jīng)常有香客問我灵奖,道長嚼沿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任瓷患,我火速辦了婚禮骡尽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘擅编。我一直安慰自己攀细,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布爱态。 她就那樣靜靜地躺著谭贪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锦担。 梳的紋絲不亂的頭發(fā)上俭识,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音洞渔,去河邊找鬼套媚。 笑死理盆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凑阶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼衷快,長吁一口氣:“原來是場噩夢啊……” “哼宙橱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蘸拔,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤师郑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后调窍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宝冕,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年邓萨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了地梨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缔恳,死狀恐怖宝剖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歉甚,我是刑警寧澤万细,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站纸泄,受9級特大地震影響赖钞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜聘裁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一雪营、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咧虎,春花似錦卓缰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茁彭,卻和暖如春总寒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背理肺。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工摄闸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留善镰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓年枕,卻偏偏與公主長得像炫欺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子熏兄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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

  • 整理來自互聯(lián)網(wǎng) 1品洛,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境摩桶,java的開發(fā)工具...
    Ncompass閱讀 1,538評論 0 6
  • 在一個(gè)方法內(nèi)部定義的變量都存儲在棧中桥状,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后,其對應(yīng)的棧就會被回收硝清,此時(shí)辅斟,在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,417評論 1 14
  • 一:java概述: 1,JDK:Java Development Kit芦拿,java的開發(fā)和運(yùn)行環(huán)境士飒,java的開發(fā)...
    慕容小偉閱讀 1,788評論 0 10
  • 一:java概述:1,JDK:Java Development Kit蔗崎,java的開發(fā)和運(yùn)行環(huán)境变汪,java的開發(fā)工...
    ZaneInTheSun閱讀 2,650評論 0 11
  • Java8張圖 11、字符串不變性 12蚁趁、equals()方法裙盾、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,702評論 0 11