java 多線程總結(jié)篇3之——生命周期和線程同步

線程的生命周期全在一張圖中渺尘,理解此圖是基本:


線程生命狀態(tài)

一茎用、新建和就緒狀態(tài)

當(dāng)程序使用new關(guān)鍵字創(chuàng)建了一個(gè)線程之后,該線程就處于新建狀態(tài)安疗,此時(shí)它和其他的Java對(duì)象一樣,僅僅由Java虛擬機(jī)為其分配內(nèi)存够委,并初始化其成員變量的值荐类。此時(shí)的線程對(duì)象沒(méi)有表現(xiàn)出任何線程的動(dòng)態(tài)特征,程序也不會(huì)執(zhí)行線程的線程執(zhí)行體茁帽。

當(dāng)線程對(duì)象調(diào)用了start()方法之后玉罐,該線程處于就緒狀態(tài)。Java虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器潘拨,處于這個(gè)狀態(tài)中的線程并沒(méi)有開(kāi)始運(yùn)行吊输,只是表示該線程可以運(yùn)行了。至于該線程何時(shí)開(kāi)始運(yùn)行铁追,取決于JVM里線程調(diào)度器的調(diào)度季蚂。

注意:?jiǎn)?dòng)線程使用start()方法,而不是run()方法琅束。永遠(yuǎn)不要調(diào)用線程對(duì)象的run()方法扭屁。調(diào)用start方法來(lái)啟動(dòng)線程,系統(tǒng)會(huì)把該run()方法當(dāng)成線程執(zhí)行體來(lái)處理涩禀;但如果直按調(diào)用線程對(duì)象的run()方法料滥,則run()方法立即就會(huì)被執(zhí)行,而且在run()方法返回之前其他線程無(wú)法并發(fā)執(zhí)行艾船。也就是說(shuō)葵腹,系統(tǒng)把線程對(duì)象當(dāng)成一個(gè)普通對(duì)象,而run()方法也是一個(gè)普通方法屿岂,而不是線程執(zhí)行體践宴。需要指出的是,調(diào)用了線程的run()方法之后爷怀,該線程已經(jīng)不再處于新建狀態(tài)阻肩,不要再次調(diào)用線程對(duì)象的start()方法。只能對(duì)處于新建狀態(tài)的線程調(diào)用start()方法霉撵,否則將引發(fā)IllegaIThreadStateExccption異常磺浙。
調(diào)用線程對(duì)象的start()方法之后洪囤,該線程立即進(jìn)入就緒狀態(tài)——就緒狀態(tài)相當(dāng)于"等待執(zhí)行",但該線程并未真正進(jìn)入運(yùn)行狀態(tài)撕氧。如果希望調(diào)用子線程的start()方法后子線程立即開(kāi)始執(zhí)行瘤缩,程序可以使用Thread.sleep(1)來(lái)讓當(dāng)前運(yùn)行的線程(主線程)睡眠1毫秒,1毫秒就夠了伦泥,因?yàn)樵谶@1毫秒內(nèi)CPU不會(huì)空閑剥啤,它會(huì)去執(zhí)行另一個(gè)處于就緒狀態(tài)的線程,這樣就可以讓子線程立即開(kāi)始執(zhí)行不脯。

二府怯、運(yùn)行和阻塞狀態(tài)

2.1 線程調(diào)度

如果處于就緒狀態(tài)的線程獲得了CPU,開(kāi)始執(zhí)行run()方法的線程執(zhí)行體防楷,則該線程處于運(yùn)行狀態(tài)牺丙,如果計(jì)算機(jī)只有一個(gè)CPU。那么在任何時(shí)刻只有一個(gè)線程處于運(yùn)行狀態(tài)复局,當(dāng)然在一個(gè)多處理器的機(jī)器上冲簿,將會(huì)有多個(gè)線程并行執(zhí)行;當(dāng)線程數(shù)大于處理器數(shù)時(shí)亿昏,依然會(huì)存在多個(gè)線程在同一個(gè)CPU上輪換的現(xiàn)象峦剔。

當(dāng)一個(gè)線程開(kāi)始運(yùn)行后,它不可能一直處于運(yùn)行狀態(tài)(除非它的線程執(zhí)行體足夠短角钩,瞬間就執(zhí)行結(jié)束了)吝沫。線程在運(yùn)行過(guò)程中需要被中斷,目的是使其他線程獲得執(zhí)行的機(jī)會(huì)递礼,線程調(diào)度的細(xì)節(jié)取決于底層平臺(tái)所采用的策略惨险。對(duì)于采用搶占式策略的系統(tǒng)而言,系統(tǒng)會(huì)給每個(gè)可執(zhí)行的線程一個(gè)小時(shí)間段來(lái)處理任務(wù)宰衙;當(dāng)該時(shí)間段用完后平道,系統(tǒng)就會(huì)剝奪該線程所占用的資源,讓其他線程獲得執(zhí)行的機(jī)會(huì)供炼。在選擇下一個(gè)線程時(shí),系統(tǒng)會(huì)考慮線程的優(yōu)先級(jí)窘疮。

所有現(xiàn)代的桌面和服務(wù)器操作系統(tǒng)都采用搶占式調(diào)度策略袋哼,但一些小型設(shè)備如手機(jī)則可能采用協(xié)作式調(diào)度策略,在這樣的系統(tǒng)中闸衫,只有當(dāng)一個(gè)線程調(diào)用了它的sleep()yield()方法后才會(huì)放棄所占用的資源——也就是必須由該線程主動(dòng)放棄所占用的資源涛贯。

2.2 線程阻塞

當(dāng)發(fā)生如下情況時(shí),線程將會(huì)進(jìn)入阻塞狀態(tài)
① 線程調(diào)用sleep()方法主動(dòng)放棄所占用的處理器資源
② 線程調(diào)用了一個(gè)阻塞式IO方法蔚出,在該方法返回之前弟翘,該線程被阻塞
③ 線程試圖獲得一個(gè)同步監(jiān)視器虫腋,但該同步監(jiān)視器正被其他線程所持有。關(guān)于同步監(jiān)視器的知識(shí)稀余、后面將存更深入的介紹
④ 線程在等待某個(gè)通知(notify)
⑤ 程序調(diào)用了線程的suspend()方法將該線程掛起悦冀。但這個(gè)方法容易導(dǎo)致死鎖,所以應(yīng)該盡量避免使用該方法

當(dāng)前正在執(zhí)行的線程被阻塞之后睛琳,其他線程就可以獲得執(zhí)行的機(jī)會(huì)盒蟆。被阻塞的線程會(huì)在合適的時(shí)候重新進(jìn)入就緒狀態(tài),注意是就緒狀態(tài)而不是運(yùn)行狀態(tài)师骗。也就是說(shuō)历等,被阻塞線程的阻塞解除后,必須重新等待線程調(diào)度器再次調(diào)度它辟癌。

2.3 解除阻塞

針對(duì)上面幾種情況寒屯,當(dāng)發(fā)生如下特定的情況時(shí)可以解除上面的阻塞,讓該線程重新進(jìn)入就緒狀態(tài):
① 調(diào)用sleep()方法的線程經(jīng)過(guò)了指定時(shí)間黍少。
② 線程調(diào)用的阻塞式IO方法已經(jīng)返回浩螺。
③ 線程成功地獲得了試圖取得的同步監(jiān)視器。
④ 線程正在等待某個(gè)通知時(shí)仍侥,其他線程發(fā)出了個(gè)通知要出。
⑤ 處于掛起狀態(tài)的線程被調(diào)甩了resume()恢復(fù)方法。
線程從阻塞狀態(tài)只能進(jìn)入就緒狀態(tài)农渊,無(wú)法直接進(jìn)入運(yùn)行狀態(tài)患蹂。而就緒和運(yùn)行狀態(tài)之間的轉(zhuǎn)換通常不受程序控制,而是由系統(tǒng)線程調(diào)度所決定砸紊。當(dāng)處于就緒狀態(tài)的線程獲得處理器資源時(shí)传于,該線程進(jìn)入運(yùn)行狀態(tài);當(dāng)處于運(yùn)行狀態(tài)的線程失去處理器資源時(shí)醉顽,該線程進(jìn)入就緒狀態(tài)沼溜。但有一個(gè)方法例外,調(diào)用yield()方法可以讓運(yùn)行狀態(tài)的線程轉(zhuǎn)入就緒狀態(tài)游添。關(guān)于yield()方法后面有更詳細(xì)的介紐系草。

三、線程死亡

3.1 死亡狀態(tài)

線程會(huì)以如下3種方式結(jié)束唆涝,結(jié)束后就處于死亡狀態(tài):
run()call()方法執(zhí)行完成找都,線程正常結(jié)束。
② 線程拋出一個(gè)未捕獲的ExceptionError廊酣。
③ 直接調(diào)用該線程stop()方法來(lái)結(jié)束該線程——該方法容易導(dǎo)致死鎖能耻,通常不推薦使用。

四、重難點(diǎn)考察

1. 有哪些不同的線程生命周期晓猛?

當(dāng)線程被創(chuàng)建并啟動(dòng)以后饿幅,它既不是一啟動(dòng)就進(jìn)入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)戒职。在線程的生命周期中栗恩,它要經(jīng)過(guò)新建(New)、就緒(Runnable)帕涌、運(yùn)行(Running)摄凡、阻塞(Blocked)和死亡(Dead)5種狀態(tài)。尤其是當(dāng)線程啟動(dòng)以后蚓曼,它不可能一直"霸占"著CPU獨(dú)自運(yùn)行亲澡,所以CPU需要在多條線程之間切換,于是線程狀態(tài)也會(huì)多次在運(yùn)行纫版、阻塞之間切換

1. 新建狀態(tài)床绪,當(dāng)程序使用new關(guān)鍵字創(chuàng)建了一個(gè)線程之后,該線程就處于新建狀態(tài)其弊,此時(shí)僅由JVM為其分配內(nèi)存癞己,并初始化其成員變量的值

2. 就緒狀態(tài),當(dāng)線程對(duì)象調(diào)用了start()方法之后梭伐,該線程處于就緒狀態(tài)痹雅。Java虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器,等待調(diào)度運(yùn)行

3. 運(yùn)行狀態(tài)糊识,如果處于就緒狀態(tài)的線程獲得了CPU绩社,開(kāi)始執(zhí)行run()方法的線程執(zhí)行體,則該線程處于運(yùn)行狀態(tài)

4. 阻塞狀態(tài)赂苗,當(dāng)處于運(yùn)行狀態(tài)的線程失去所占用資源之后愉耙,便進(jìn)入阻塞狀態(tài).

5. 轉(zhuǎn)換過(guò)程, 在線程的生命周期當(dāng)中,線程的各種狀態(tài)的轉(zhuǎn)換過(guò)程

2.線程狀態(tài)拌滋,BLOCKED 和 WAITING 有什么區(qū)別

線程可以通過(guò)wait,join,LockSupport.park方式進(jìn)入wating狀態(tài)朴沿,進(jìn)入wating狀態(tài)的線程等待喚醒(notifynotifyAll)才有機(jī)會(huì)獲取cpu的時(shí)間片段來(lái)繼續(xù)執(zhí)行。線程的 blocked狀態(tài)往往是無(wú)法進(jìn)入同步方法/代碼塊來(lái)完成的败砂。這是因?yàn)闊o(wú)法獲取到與同步方法/代碼塊相關(guān)聯(lián)的鎖赌渣。與wating狀態(tài)相關(guān)聯(lián)的是等待隊(duì)列,與blocked狀態(tài)相關(guān)的是同步隊(duì)列吠卷,一個(gè)線程由等待隊(duì)列遷移到同步隊(duì)列時(shí)锡垄,線程狀態(tài)將會(huì)由wating轉(zhuǎn)化為blocked〖栏簦可以這樣說(shuō),blocked狀態(tài)是處于wating狀態(tài)的線程重新煥發(fā)生命力的必由之路。

不過(guò)個(gè)人覺(jué)得實(shí)際上不用可以區(qū)分兩者, 因?yàn)閮烧叨紩?huì)暫停線程的執(zhí)行. 兩者的區(qū)別是: 進(jìn)入waiting狀態(tài)是線程主動(dòng)的, 而進(jìn)入blocked狀態(tài)是被動(dòng)的. 更進(jìn)一步的說(shuō), 進(jìn)入blocked狀態(tài)是在同步(synchronized代碼之外), 而進(jìn)入waiting狀態(tài)是在同步代碼之內(nèi).

3.ThreadLocal用途是什么疾渴,原理是什么千贯,用的時(shí)候要注意什么?

在多線程程序中搞坝,同一個(gè)線程在某個(gè)時(shí)間段只能處理一個(gè)任務(wù)搔谴,我們希望在這個(gè)時(shí)間段內(nèi),任務(wù)的某些變量能夠和處理它的線程進(jìn)行綁定,,在任務(wù)需要使用這個(gè)變量的時(shí)候桩撮,這個(gè)變量能夠方便的從線程中取出來(lái)敦第。ThreadLocal能很好的滿足這個(gè)需求,用ThreadLocal變量的程序看起來(lái)也會(huì)簡(jiǎn)潔很多店量,因?yàn)闇p少了變量在程序中的傳遞芜果,每個(gè)運(yùn)行的線程都會(huì)有一個(gè)類(lèi)型為ThreadLocalThreadLocalMapmap融师,這個(gè)map就是用來(lái)存儲(chǔ)與這個(gè)線程綁定的變量,mapkey就是ThreadLocal對(duì)象右钾,value就是線程正在執(zhí)行的任務(wù)中的某個(gè)變量的包裝類(lèi)Entry、在使用ThreadLocal對(duì)象旱爆,盡量使用static舀射,不然會(huì)使線程的ThreadLocalMap產(chǎn)生太多Entry,從而造成內(nèi)存泄露怀伦。

4.Java中用到的線程調(diào)度算法是什么脆烟?
5.什么是多線程中的上下文切換?

即使是單核CPU也支持多線程執(zhí)行代碼房待,CPU通過(guò)給每個(gè)線程分配CPU時(shí)間片來(lái)實(shí)現(xiàn)這個(gè)機(jī)制邢羔。時(shí)間片是CPU分配給各個(gè)線程的時(shí)間,因?yàn)闀r(shí)間片非常短吴攒,所以CPU通過(guò)不停地切換線程執(zhí)行张抄,讓我們感覺(jué)多個(gè)線程時(shí)同時(shí)執(zhí)行的,時(shí)間片一般是幾十毫秒(ms)洼怔。CPU通過(guò)時(shí)間片分配算法來(lái)循環(huán)執(zhí)行任務(wù)署惯,當(dāng)前任務(wù)執(zhí)行一個(gè)時(shí)間片后會(huì)切換到下一個(gè)任務(wù)。但是镣隶,在切換前會(huì)保存上一個(gè)任務(wù)的狀態(tài)极谊,以便下次切換回這個(gè)任務(wù)時(shí),可以再次加載這個(gè)任務(wù)的狀態(tài)安岂,從任務(wù)保存到再加載的過(guò)程就是一次上下文切換轻猖。

6、你對(duì)線程優(yōu)先級(jí)的理解是什么域那?

每一個(gè)線程都是有優(yōu)先級(jí)的, 一般來(lái)說(shuō), 高優(yōu)先級(jí)的線程在運(yùn)行時(shí)會(huì)具有優(yōu)先權(quán), 但這依賴(lài)于線程調(diào)度的實(shí)現(xiàn), 這個(gè)實(shí)現(xiàn)是和操作系統(tǒng)相關(guān)的. 我們可以定義線程的優(yōu)先級(jí)getPriority() setPriority(), 但是這并不能保證高優(yōu)先級(jí)的線程會(huì)在低優(yōu)先級(jí)的線程前執(zhí)行. 縣城優(yōu)先級(jí)是一個(gè)int變量, 1代表最低, 10代表最高咙边。

7、什么是線程調(diào)度器 (Thread Scheduler) 和時(shí)間分片 (Time Slicing)?

線程調(diào)度器是一個(gè)操作系統(tǒng)服務(wù), 它負(fù)責(zé)為Runnable狀態(tài)的線程分配CPU時(shí)間, 一旦我們創(chuàng)建一個(gè)線程并啟動(dòng)它, 它的執(zhí)行便依賴(lài)于線程調(diào)度器的實(shí)現(xiàn)。時(shí)間分片是指將可用的CPU時(shí)間分配給可用的Runnable線程的過(guò)程败许。分配的CPU時(shí)間可以基于線程的優(yōu)先級(jí)或者線程的等待時(shí)間, 線程調(diào)度并不受到Java虛擬機(jī)控制, 所以由應(yīng)用程序來(lái)控制它是更好的選擇(也就是說(shuō)不要讓你的程序依賴(lài)于線程優(yōu)先級(jí))王带。

二、線程同步

java允許多線程并發(fā)控制市殷,當(dāng)多個(gè)線程同時(shí)操作一個(gè)可共享的資源變量時(shí)(如數(shù)據(jù)的增刪改查)愕撰, 將會(huì)導(dǎo)致數(shù)據(jù)不準(zhǔn)確,相互之間產(chǎn)生沖突醋寝,因此加入同步鎖以避免在該線程沒(méi)有完成操作之前搞挣,被其他線程的調(diào)用, 從而保證了該變量的唯一性和準(zhǔn)確性音羞。

1囱桨、 請(qǐng)說(shuō)出你所知的線程同步的方法

1.同步方法。即有synchronized關(guān)鍵字修飾的方法黄选。 由于java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖蝇摸,當(dāng)用此關(guān)鍵字修飾方法時(shí), 內(nèi)置鎖會(huì)保護(hù)整個(gè)方法办陷。在調(diào)用該方法前貌夕,需要獲得內(nèi)置鎖,否則就處于阻塞狀態(tài)民镜。代碼如:

public synchronized void save(){}
//注:synchronized關(guān)鍵字也可以修飾靜態(tài)方法啡专,此時(shí)如果調(diào)用該靜態(tài)方法,將會(huì)鎖住整個(gè)類(lèi)

2.同步代碼塊制圈,即有synchronized關(guān)鍵字修飾的語(yǔ)句塊们童。 被該關(guān)鍵字修飾的語(yǔ)句塊會(huì)自動(dòng)被加上內(nèi)置鎖,從而實(shí)現(xiàn)同步

//代碼如:
synchronized(object){
}

注:同步是一種高開(kāi)銷(xiāo)的操作鲸鹦,因此應(yīng)該盡量減少同步的內(nèi)容慧库。 通常沒(méi)有必要同步整個(gè)方法,使用synchronized代碼塊同步關(guān)鍵代碼即可馋嗜。

/**
     * 線程同步的運(yùn)用
     */
    public class SynchronizedThread {

        class Bank {

            private int account = 100;

            public int getAccount() {
                return account;
            }

            /**
             * 用同步方法實(shí)現(xiàn)
             * @param money
             */
            public synchronized void save(int money) {
                account += money;
            }

            /**
             * 用同步代碼塊實(shí)現(xiàn)
             * @param money
             */
            public void save1(int money) {
                synchronized (this) {
                    account += money;
                }
            }
        }

        class NewThread implements Runnable {
            private Bank bank;

            public NewThread(Bank bank) {
                this.bank = bank;
            }

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    // bank.save1(10);
                    bank.save(10);
                    System.out.println(i + "賬戶(hù)余額為:" + bank.getAccount());
                }
            }
        }

        /**
         * 建立線程齐板,調(diào)用內(nèi)部類(lèi)
         */
        public void useThread() {
            Bank bank = new Bank();
            NewThread new_thread = new NewThread(bank);
            System.out.println("線程1");
            Thread thread1 = new Thread(new_thread);
            thread1.start();
            System.out.println("線程2");
            Thread thread2 = new Thread(new_thread);
            thread2.start();
        }

        public static void main(String[] args) {
            SynchronizedThread st = new SynchronizedThread();
            st.useThread();
        }
    }

3.使用特殊域變量(volatile)實(shí)現(xiàn)線程同步
a.volatile關(guān)鍵字為域變量的訪問(wèn)提供了一種免鎖機(jī)制,
b.使用volatile修飾域相當(dāng)于告訴虛擬機(jī)該域可能會(huì)被其他線程更新葛菇,
c.因此每次使用該域就要重新計(jì)算甘磨,而不是使用寄存器中的值
d.volatile不會(huì)提供任何原子操作,它也不能用來(lái)修飾final類(lèi)型的變量

//只給出要修改的代碼眯停,其余代碼與上同
        class Bank {
            //需要同步的變量加上volatile
            private volatile int account = 100;

            public int getAccount() {
                return account;
            }
            //這里不再需要synchronized 
            public void save(int money) {
                account += money;
            }
        }

注:多線程中的非同步問(wèn)題主要出現(xiàn)在對(duì)域的讀寫(xiě)上济舆,如果讓域自身避免這個(gè)問(wèn)題,則就不需要修改操作該域的方法莺债。 用final域滋觉,有鎖保護(hù)的域和volatile域可以避免非同步的問(wèn)題签夭。
4.使用重入鎖實(shí)現(xiàn)線程同步,在JavaSE5.0中新增了一個(gè)java.util.concurrent包來(lái)支持同步椎瘟。 ReentrantLock類(lèi)是可重入覆致、互斥侄旬、實(shí)現(xiàn)了Lock接口的鎖肺蔚, 它與使用synchronized方法和快具有相同的基本行為和語(yǔ)義,并且擴(kuò)展了其能力ReenreantLock類(lèi)的常用方法有:

ReentrantLock() : 創(chuàng)建一個(gè)ReentrantLock實(shí)例
lock() : 獲得鎖
unlock() : 釋放鎖

注:ReentrantLock()還有一個(gè)可以創(chuàng)建公平鎖的構(gòu)造方法儡羔,但由于能大幅度降低程序運(yùn)行效率宣羊,不推薦使用
5.使用局部變量實(shí)現(xiàn)線程同步。如果使用ThreadLocal管理變量汰蜘,則每一個(gè)使用該變量的線程都獲得該變量的副本仇冯, 副本之間相互獨(dú)立,這樣每一個(gè)線程都可以隨意修改自己的變量副本族操,而不會(huì)對(duì)其他線程產(chǎn)生影響苛坚。
ThreadLocal 類(lèi)的常用方法

ThreadLocal() : 創(chuàng)建一個(gè)線程本地變量
get() : 返回此線程局部變量的當(dāng)前線程副本中的值
initialValue() : 返回此線程局部變量的當(dāng)前線程的"初始值"
set(T value) : 將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為value

注:ThreadLocal與同步機(jī)制:a.ThreadLocal與同步機(jī)制都是為了解決多線程中相同變量的訪問(wèn)沖突問(wèn)題。 b.前者采用以"空間換時(shí)間"的方法色难,后者采用以"時(shí)間換空間"的方式
6.使用阻塞隊(duì)列實(shí)現(xiàn)線程同步泼舱,7.使用原子變量實(shí)現(xiàn)線程同步****

《關(guān)于線程同步的7種方式》

2、 synchronized 的原理是什么

Synchronized的語(yǔ)義底層是通過(guò)一個(gè)monitor(監(jiān)視器鎖)來(lái)實(shí)現(xiàn)的枷莉。Synchronized進(jìn)過(guò)編譯娇昙,會(huì)在同步塊的前后分別形成monitorentermonitorexit這個(gè)兩個(gè)字節(jié)碼指令。在執(zhí)行monitorenter指令時(shí)笤妙,首先要嘗試獲取對(duì)象鎖冒掌。如果這個(gè)對(duì)象沒(méi)被鎖定,或者當(dāng)前線程已經(jīng)擁有了那個(gè)對(duì)象鎖蹲盘,把鎖的計(jì)算器加1股毫,相應(yīng)的,在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)算器就減1召衔,當(dāng)計(jì)算器為0時(shí)铃诬,鎖就被釋放了。如果獲取對(duì)象鎖失敗薄嫡,那當(dāng)前線程就要阻塞氧急,直到對(duì)象鎖被另一個(gè)線程釋放為止。

monitorenter :
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:
? If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
? If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
? If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

每個(gè)對(duì)象有一個(gè)監(jiān)視器鎖(monitor)毫深。當(dāng)monitor被占用時(shí)就會(huì)處于鎖定狀態(tài)吩坝,線程執(zhí)行monitorenter指令時(shí)嘗試獲取monitor的所有權(quán),過(guò)程如下:
1哑蔫、如果monitor的進(jìn)入數(shù)為0钉寝,則該線程進(jìn)入monitor弧呐,然后將進(jìn)入數(shù)設(shè)置為1,該線程即為monitor的所有者嵌纲。
2俘枫、如果線程已經(jīng)占有該monitor,只是重新進(jìn)入逮走,則進(jìn)入monitor的進(jìn)入數(shù)加1.
3.如果其他線程已經(jīng)占用了monitor鸠蚪,則該線程進(jìn)入阻塞狀態(tài),直到monitor的進(jìn)入數(shù)為0师溅,再重新嘗試獲取monitor的所有權(quán)茅信。

monitorexit: 
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

執(zhí)行monitorexit的線程必須是objectref所對(duì)應(yīng)的monitor的所有者。指令執(zhí)行時(shí)墓臭,monitor的進(jìn)入數(shù)減1蘸鲸,如果減1后進(jìn)入數(shù)為0,那線程退出monitor窿锉,不再是這個(gè)monitor的所有者酌摇。其他被這個(gè)monitor阻塞的線程可以嘗試去獲取這個(gè) monitor 的所有權(quán)。

《深入分析Synchronized》 《Java并發(fā)編程之——Synchronized》

3嗡载、 synchronized 和ReentrantLock有什么不同

相似點(diǎn):這兩種同步方式有很多相似之處窑多,它們都是加鎖方式同步乡恕,而且都是阻塞式的同步腕窥,也就是說(shuō)當(dāng)如果一個(gè)線程獲得了對(duì)象鎖,進(jìn)入了同步塊态鳖,其他訪問(wèn)該同步塊的線程都必須阻塞在同步塊外面等待判沟,而進(jìn)行線程阻塞和喚醒的代價(jià)是比較高的(操作系統(tǒng)需要在用戶(hù)態(tài)與內(nèi)核態(tài)之間來(lái)回切換耿芹,代價(jià)很高,不過(guò)可以通過(guò)對(duì)鎖優(yōu)化進(jìn)行改善)挪哄。

區(qū)別:這兩種方式最大區(qū)別就是對(duì)于Synchronized來(lái)說(shuō)吧秕,它是java語(yǔ)言的關(guān)鍵字,是原生語(yǔ)法層面的互斥迹炼,需要jvm實(shí)現(xiàn)砸彬。而ReentrantLock它是JDK 1.5之后提供的API層面的互斥鎖,需要lock()unlock()方法配合try/finally語(yǔ)句塊來(lái)完成斯入。

總的來(lái)說(shuō)砂碉,Lock提供了比synchronized更多的功能。但是要注意以下幾點(diǎn):

1)Lock不是Java語(yǔ)言?xún)?nèi)置的刻两,synchronized是Java語(yǔ)言的關(guān)鍵字增蹭,因此是內(nèi)置特性。Lock是一個(gè)類(lèi)磅摹,通過(guò)這個(gè)類(lèi)可以實(shí)現(xiàn)同步訪問(wèn)滋迈;

2)Locksynchronized有一點(diǎn)非常大的不同霎奢,采用synchronized不需要用戶(hù)去手動(dòng)釋放鎖,當(dāng)synchronized方法或者synchronized代碼塊執(zhí)行完之后饼灿,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用幕侠;而Lock則必須要用戶(hù)去手動(dòng)釋放鎖,如果沒(méi)有主動(dòng)釋放鎖碍彭,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象晤硕。
synchronized在發(fā)生異常時(shí),會(huì)自動(dòng)釋放線程占有的鎖硕旗,因此不會(huì)導(dǎo)致死鎖現(xiàn)象發(fā)生窗骑;而Lock在發(fā)生異常時(shí),如果沒(méi)有主動(dòng)通過(guò)unLock()去釋放鎖漆枚,則很可能造成死鎖現(xiàn)象,因此使用Lock時(shí)需要在finally塊中釋放鎖抵知;

3)Lock可以讓等待鎖的線程響應(yīng)中斷墙基,而synchronized卻不行,使用synchronized時(shí)刷喜,等待的線程會(huì)一直等待下去残制,不能夠響應(yīng)中斷;

4)通過(guò)Lock可以知道有沒(méi)有成功獲取鎖掖疮,而synchronized卻無(wú)法辦到初茶。

5)Lock可以提高多個(gè)線程進(jìn)行讀操作的效率。

在性能上來(lái)說(shuō)浊闪,如果競(jìng)爭(zhēng)資源不激烈恼布,兩者的性能是差不多的,而當(dāng)競(jìng)爭(zhēng)資源非常激烈時(shí)(即有大量線程同時(shí)競(jìng)爭(zhēng))搁宾,此時(shí)Lock的性能要遠(yuǎn)遠(yuǎn)優(yōu)于synchronized折汞。所以說(shuō),在具體使用時(shí)要根據(jù)適當(dāng)情況選擇盖腿。

《java兩種線程同步方式的區(qū)別——Synchronized和ReentrantLock》

4爽待、什么場(chǎng)景下可以使用 volatile 替換 synchronized

只需要保證共享資源的可見(jiàn)性的時(shí)候可以使用volatile替代,synchronized保證可操作的原子性一致性和可見(jiàn)性翩腐。volatile適用于新值不依賴(lài)于舊值的情形

5鸟款、有T1,T2茂卦,T3三個(gè)線程何什,怎么確保它們按順序執(zhí)行?怎樣保證T2在T1執(zhí)行完后執(zhí)行疙筹,T3在T2執(zhí)行完后執(zhí)行

這里考察的主要知識(shí)點(diǎn)就是線程同步機(jī)制和鎖的問(wèn)題富俄,《Java 指定線程執(zhí)行順序(三種方式)》
另外關(guān)于執(zhí)行順序的問(wèn)題禁炒,可以有很多的考察點(diǎn),可以刷刷這個(gè)博客《java指定線程執(zhí)行的順序》

6霍比、同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么

這個(gè)問(wèn)題坑了很多Java程序員幕袱,若你能想到鎖是否釋放這條線索來(lái)回答還有點(diǎn)希望答對(duì)。無(wú)論你的同步塊是正常還是異常退出的悠瞬,里面的線程都會(huì)釋放鎖们豌,所以對(duì)比鎖接口我更喜歡同步塊,因?yàn)樗挥梦一ㄙM(fèi)精力去釋放鎖浅妆,該功能可以在finally block里釋放鎖實(shí)現(xiàn)望迎。

7、當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的synchronized 方法A 之后凌外,其它線程是否可進(jìn)入此對(duì)象的synchronized 方法B

不能辩尊。其它線程只能訪問(wèn)該對(duì)象的非同步方法,同步方法則不能進(jìn)入康辑。因?yàn)榉庆o態(tài)方法上的synchronized修飾符要求執(zhí)行方法時(shí)要獲得對(duì)象的鎖摄欲,如果已經(jīng)進(jìn)入A方法說(shuō)明對(duì)象鎖已經(jīng)被取走,那么試圖進(jìn)入B方法的線程就只能在等鎖池(注意不是等待池哦 )中等待對(duì)象的鎖疮薇。

8胸墙、使用 synchronized 修飾靜態(tài)方法和非靜態(tài)方法有什么區(qū)別

static方法前加synchronizedstatic:靜態(tài)方法屬于類(lèi)方法,它屬于這個(gè)類(lèi)按咒,獲取到的鎖迟隅,是屬于類(lèi)的鎖。 在普通方法前加synchronizedstatic:非static方法獲取到的鎖励七,是屬于當(dāng)前對(duì)象的鎖智袭。 結(jié)論:類(lèi)鎖和對(duì)象鎖不同,他們之間不會(huì)產(chǎn)生互斥呀伙。

9补履、如何從給定集合那里創(chuàng)建一個(gè) synchronized 的集合

我們可以使用Collections.synchronizedCollection(Collection c)根據(jù)指定集合來(lái)獲取一個(gè)synchronized(線程安全的)集合。比如HashMap可以這樣來(lái)實(shí)現(xiàn)線程安全:

Map m = Collections.synchronizedMap(new HashMap);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剿另,一起剝皮案震驚了整個(gè)濱河市箫锤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雨女,老刑警劉巖谚攒,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異氛堕,居然都是意外死亡馏臭,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)括儒,“玉大人绕沈,你說(shuō)我怎么就攤上這事“镅埃” “怎么了乍狐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)固逗。 經(jīng)常有香客問(wèn)我浅蚪,道長(zhǎng),這世上最難降的妖魔是什么烫罩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任惜傲,我火速辦了婚禮,結(jié)果婚禮上贝攒,老公的妹妹穿的比我還像新娘盗誊。我一直安慰自己,他們只是感情好饿这,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布浊伙。 她就那樣靜靜地躺著,像睡著了一般长捧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吻贿,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天串结,我揣著相機(jī)與錄音,去河邊找鬼舅列。 笑死肌割,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的帐要。 我是一名探鬼主播把敞,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼榨惠!你這毒婦竟也來(lái)了奋早?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赠橙,失蹤者是張志新(化名)和其女友劉穎耽装,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體期揪,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掉奄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凤薛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姓建。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诞仓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出速兔,到底是詐尸還是另有隱情墅拭,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布憨栽,位于F島的核電站帜矾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屑柔。R本人自食惡果不足惜屡萤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掸宛。 院中可真熱鬧死陆,春花似錦、人聲如沸唧瘾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)饰序。三九已至领虹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間求豫,已是汗流浹背塌衰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝠嘉,地道東北人最疆。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蚤告,于是被迫代替她去往敵國(guó)和親努酸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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