Java Synchronized的使用細(xì)節(jié)

有這樣一個(gè)場景,一個(gè)銀行賬戶有一個(gè)活期儲(chǔ)蓄余額和定期儲(chǔ)蓄余額。銀行類如下:

public class Account {
        //活期存款
        private int currentDeposit = 0;
        //定期存款
        private int fixedDeposit = 0;
        
        public synchronized void addCurrentDeposit(int amount) {
                this.currentDeposit += amount;
        }
        
        public synchronized void addFixedDeposit(int amount) {
                this.currentDeposit += amount;
        }
        
        public void read() {
            System.out.println("活期存款是:" + currentDeposit + " 定期存款是:" + fixedDeposit);
        }
    }
  • 問題1:當(dāng)一個(gè)線程給活期增加余額時(shí)候(執(zhí)行addCurrentDeposit方法)作烟,另一個(gè)線程能不能給定期增加額度(執(zhí)行addFixedDeposit方法)
  • 問題2:當(dāng)一個(gè)線程給活期增加余額時(shí)候,能不能執(zhí)行read方法珍促?
  • 問題3:假如要實(shí)現(xiàn)給活期存款時(shí)候也可以給定期存款,該怎么設(shè)計(jì)Account類剩愧?

問題1和問題2

設(shè)計(jì)這樣一個(gè)場景猪叙。三個(gè)線程分別操作同一個(gè)Account對象的3個(gè)方法addCurrentDeposit,addCurrentDeposit仁卷,read穴翩。執(zhí)行addCurrentDeposit需要20秒。然后通過實(shí)驗(yàn)結(jié)果驗(yàn)證锦积。

  • 如果執(zhí)行addCurrentDeposit(需要20秒)過程中,addCurrentDeposit沒有執(zhí)行芒帕,同時(shí)read也沒有執(zhí)行,都是等addCurrentDeposit執(zhí)行完再執(zhí)行丰介,那么就可以得出:如果一個(gè)線程訪問一個(gè)對象的synchronized方法背蟆,其他線程不可以訪問該對象的所有其他方法
  • 如果執(zhí)行addCurrentDeposit(需要20秒)過程中基矮,addCurrentDeposit沒有執(zhí)行淆储,而read方法執(zhí)行了,則可以得出結(jié)論:如果一個(gè)線程訪問一個(gè)對象的synchronized方法家浇,其他線程不可以訪問該對象的其他synchronized方法本砰,其他非synchronized方法可以正常訪問

給出設(shè)計(jì)的實(shí)驗(yàn)代碼;

package synchronozedtest;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: gethin
 * @create: 2018-07-03 16:55
 * @description:
 **/
public class SynTest {
    private static Account account = new Account();
    
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        Thread t1 = new Thread(new AddCurrentDepositTask());
        Thread t2 = new Thread(new AddFixedDepositTask());
        Thread t3 = new Thread(new ReadTask());
        executor.submit(t1);
        //線程1提交后等待1秒钢悲,再提交線程2和線程3
        Thread.sleep(1000);
        executor.submit(t2);
        executor.submit(t3);
        executor.shutdown();
    }
    
    private static class AddCurrentDepositTask implements Runnable {
        
        @Override
        public void run() {
            account.addCurrentDeposit(1);
        }
    }
    
    private static class AddFixedDepositTask implements Runnable {
        
        @Override
        public void run() {
            account.addFixedDeposit(2);
        }
    }
    
    private static class ReadTask implements Runnable {
        
        @Override
        public void run() {
            account.read();
        }
    }
    
    
    private static class Account {
        //活期存款
        private int currentDeposit = 0;
        //定期存款
        private int fixedDeposit = 0;
        
        public synchronized void addCurrentDeposit(int amount) {
            long start=System.currentTimeMillis();
            System.out.println("活期存錢");
            this.currentDeposit += amount;
            try {
                //讓線程等待20秒点额,線程獲得鎖后必須等待100秒舔株,才能釋放鎖
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end=System.currentTimeMillis();
            System.out.println("執(zhí)行活期存款用了"+(end-start)/1000+"秒");
        }
        
        public synchronized void addFixedDeposit(int amount) {
            System.out.println("定期存錢");
            this.fixedDeposit += amount;
        }
        
        public void read() {
            System.out.println("活期存款是:" + currentDeposit + " 定期存款是:" + fixedDeposit);
        }
    }
}

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


image

從上圖可以看出,執(zhí)行addCurrentDeposit過程中还棱,addCurrentDeposit沒有執(zhí)行载慈,而read方法執(zhí)行了

所以可以得出結(jié)論:如果一個(gè)線程訪問一個(gè)對象的synchronized方法珍手,其他線程不可以訪問該對象的其他synchronized方法办铡,其他非synchronized方法則可以正常訪問

再往深處問一下,為什么是這個(gè)結(jié)論琳要,為什么一個(gè)線程訪問一個(gè)對象的synchronized方法寡具,其他線程也不能再訪問該對象的其他的synchronized?

這就要考究到synchronized的實(shí)現(xiàn)原理上來稚补,JVM 是通過進(jìn)入童叠、退出對象監(jiān)視器( Monitor )來實(shí)現(xiàn)對方法、同步塊的同步的课幕。

  • 一個(gè)線程要進(jìn)去一個(gè)synchronized方法厦坛,必須獲得對象鎖,
  • 如果第一個(gè)線程獲得對象鎖乍惊,進(jìn)入其中一個(gè)synchronized方法杜秸。
  • 其他線程要進(jìn)入該對象其他synchronized方法,必須等第一個(gè)線程執(zhí)行完synchronized方法釋放掉對象鎖污桦,才能繼續(xù)執(zhí)行亩歹。


    synchronized實(shí)現(xiàn)原理

問題3

要在增加活期儲(chǔ)蓄同時(shí)增加定期儲(chǔ)蓄,可以重新設(shè)計(jì)Account,把int改成Integer對象凡橱,在addCurrentDeposit小作,addCurrentDeposit中分別同步Integer對象而不是同步Account對象。如下:

private static class Account {
        //活期存款
        private Integer currentDeposit = new Integer(0);
        //定期存款
        private Integer fixedDeposit = new Integer(0);
        
        
        public void addCurrentDeposit(int amount) {
            synchronized (currentDeposit) {
                long start = System.currentTimeMillis();
                System.out.println("活期存錢");
                this.currentDeposit += amount;
                try {
                    //讓線程等待20秒稼钩,線程獲得鎖后必須等待100秒顾稀,才能釋放鎖
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long end = System.currentTimeMillis();
                System.out.println("執(zhí)行活期存款用了" + (end - start) / 1000 + "秒");
            }
        }
        
        public void addFixedDeposit(int amount) {
            synchronized (fixedDeposit) {
                System.out.println("定期存錢");
                this.fixedDeposit += amount;
            }
        }
        
        public void read() {
            System.out.println("活期存款是:" + currentDeposit + " 定期存款是:" + fixedDeposit);
        }
    }

結(jié)果如下:


image

從結(jié)果圖可以看出在執(zhí)行活期存儲(chǔ)的20秒內(nèi),定期存錢也執(zhí)行了坝撑,很好的解決了問題3静秆。此外,
synchronized關(guān)鍵字可用于標(biāo)記四種不同類型的塊:

  1. 實(shí)例方法
  2. 靜態(tài)方法
  3. 實(shí)例方法中的代碼塊
  4. 靜態(tài)方法中的代碼塊

這些使用方式巡李,區(qū)別如下:

  • 同步普通方法抚笔,鎖的是當(dāng)前對象。

  • 同步靜態(tài)方法侨拦,鎖的是當(dāng)前 Class 對象殊橙。

  • 同步塊,鎖的是 {} 中的對象。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末膨蛮,一起剝皮案震驚了整個(gè)濱河市叠纹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敞葛,老刑警劉巖誉察,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異惹谐,居然都是意外死亡持偏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門氨肌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來综液,“玉大人,你說我怎么就攤上這事儒飒。” “怎么了檩奠?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵桩了,是天一觀的道長。 經(jīng)常有香客問我埠戳,道長井誉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任整胃,我火速辦了婚禮颗圣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘屁使。我一直安慰自己在岂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布蛮寂。 她就那樣靜靜地躺著蔽午,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酬蹋。 梳的紋絲不亂的頭發(fā)上及老,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音范抓,去河邊找鬼骄恶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匕垫,可吹牛的內(nèi)容都是我干的僧鲁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼悔捶!你這毒婦竟也來了铃慷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜕该,失蹤者是張志新(化名)和其女友劉穎犁柜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堂淡,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馋缅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绢淀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萤悴。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖皆的,靈堂內(nèi)的尸體忽然破棺而出覆履,到底是詐尸還是另有隱情,我是刑警寧澤费薄,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布硝全,位于F島的核電站,受9級特大地震影響楞抡,放射性物質(zhì)發(fā)生泄漏伟众。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一召廷、第九天 我趴在偏房一處隱蔽的房頂上張望凳厢。 院中可真熱鬧,春花似錦竞慢、人聲如沸先紫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泡孩。三九已至,卻和暖如春寺谤,著一層夾襖步出監(jiān)牢的瞬間仑鸥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工变屁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眼俊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓粟关,卻偏偏與公主長得像疮胖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

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

  • 前言 本人主要是結(jié)合《Java多線程編程核心技術(shù)》這本書的第二章內(nèi)容,對synchronized關(guān)鍵字的知識進(jìn)行梳...
    AR7_閱讀 897評論 0 4
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,386評論 8 265
  • 進(jìn)程和線程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過程中...
    小徐andorid閱讀 2,808評論 3 53
  • 靜謐的午后里我遇見你陽光重若千鈞壓彎了你的脊背連須發(fā)也像倦極了的云低聲喘息——當(dāng)我問起你的往日曾經(jīng)閃爍的眸子沉默著...
    Biobot閱讀 136評論 0 0
  • 悸動(dòng)的心 人會(huì)在一日之間改變,你信嗎钟鸵? 生命會(huì)在一瞬間變得光輝燦爛,你信嗎拦止? 歲月會(huì)突然充滿了...
    秋蕭蕭之平兒閱讀 373評論 4 2