今天剛剛參加了一次面試震放,被面試官的關(guān)于線程的提問生生碾壓了筷厘。重整旗鼓,總結(jié)一下關(guān)于線程的基礎(chǔ)知識前普,相信也可以幫到一批小伙伴的肚邢。首先分享一下基礎(chǔ)知識。南玻萬:0
線程與進程的區(qū)別
區(qū)別詳解請移步鏈接中的文章已經(jīng)把區(qū)別整理的很完善了拭卿,同學們可以自行移步查看骡湖。我在這里寫一下我自己總結(jié)的它們之間的區(qū)別:
- 它們是不同的操作系統(tǒng)管理資源的方式。
- 進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位峻厚;
- 線程是CPU進行資源分配和調(diào)度的基本單位响蕴;
- 一個程序至少有一個進程,一個進程又至少有一個線程惠桃。
- 進程有自己獨立的地址空間浦夷,一個進程崩潰后,一般不會對其他進程產(chǎn)生影響辜王。而線程只是一個進程的不同執(zhí)行路徑劈狐,線程有自己的堆棧和局部變量,但線程沒有單獨的地址空間呐馆。一個線程死掉就等于整個進程死掉肥缔,所以多進程的程序比多線程的程序更健壯。但進程切換時汹来,耗費資源較大续膳,效率要差一些改艇。
線程的狀態(tài)
下面這張圖片展示了線程的所有狀態(tài)和狀態(tài)之間的互相轉(zhuǎn)換:
- 可運行(runnable):線程創(chuàng)建后調(diào)用了.start()方法。該線程位于可運行的線程池中坟岔,等待被線程調(diào)度選中谒兄,獲得CPU的資源;
- 運行(running):可運行狀態(tài)(runnable)的線程獲得了CPU的時間片(timeslice)炮车,執(zhí)行線程內(nèi)的代碼舵变;
- 阻塞(block):阻塞狀態(tài)是指線程暫時放棄了CPU的使用權(quán),即讓出了CPU的時間片瘦穆。阻塞狀態(tài)的線程直到轉(zhuǎn)換成可運行狀態(tài)進入到可運行線程池中纪隙,才會有機會再次獲得CPU的時間片進入到運行狀態(tài)。使線程進入阻塞狀態(tài)的情況由一下幾種:
- 等待阻塞:線程執(zhí)行了wait()方法扛或,JVM會將線程放入到等待隊列(waitting queue)中绵咱。
- 同步阻塞:運行的線程在獲取對象的同步鎖時,如該同步鎖被別的線程占用熙兔,則JVM會把該線程放入到鎖池(lock pool)中悲伶。
- 其他阻塞:運行中的線程執(zhí)行了Thread.sleep()方法或b.join()方法,或者當前線程執(zhí)行到了I/O操作時住涉。JVM會把線程切換到阻塞狀態(tài)麸锉,當sleep()方法休眠結(jié)束時、b線程執(zhí)行完成后舆声,I/O操作完成后花沉,該線程又會轉(zhuǎn)換到Runnable狀態(tài)。
- 死亡(dead):線程run()媳握、main() 方法執(zhí)行結(jié)束碱屁,或者因異常退出了run()方法,則該線程結(jié)束生命周期蛾找。死亡的線程不可再次復生娩脾。
同步鎖(synchonrize)的介紹
相信很多同學應該都和我差不多只知道這個關(guān)鍵字的簡單實用,對于很多細節(jié)其實并不是很清楚打毛,這里我同大家一起分享關(guān)于同步鎖的正確使用柿赊。
首先我們大家都應該知道每個對象都有一個鎖標志,被synchonrize
關(guān)鍵字修飾的變量或方法將被上鎖幻枉,同一時刻只能被單一線程訪問碰声。當前線程訪問完數(shù)據(jù)后釋放鎖標志,其他線程才可以進行訪問展辞。這里還需要給大家補充一下線程常用方法與同步鎖之間存在的關(guān)系,以下表格可以清晰的展示這一點:
方法 | 是否釋放鎖 | 備注 |
---|---|---|
wait | 是 | wait和notify/notifyAll是成對出現(xiàn)的, 必須在synchronize塊中被調(diào)用 |
sleep | 否 | 可使低優(yōu)先級的線程獲得執(zhí)行機會 |
yield | 否 | yield方法使當前線程讓出CPU占有權(quán), 但讓出的時間是不可設(shè)定的 |
對以上方法的備注我還引入一些更具體的解釋(點擊給進入原作者的文章)万牺。如下:
sleep()
使當前線程(即調(diào)用該方法的線程)暫停執(zhí)行一段時間罗珍,讓其他線程有機會繼續(xù)執(zhí)行洽腺,但它并不釋放對象鎖。也就是說如果有synchronized同步塊覆旱,其他線程仍然不能訪問共享數(shù)據(jù)蘸朋。注意該方法要捕捉異常。
例如有兩個線程同時執(zhí)行(沒有synchronized)一個線程優(yōu)先級為MAX_PRIORITY扣唱,另一個為MIN_PRIORITY藕坯,如果沒有Sleep()方法,只有高優(yōu)先級的線程執(zhí)行完畢后噪沙,低優(yōu)先級的線程才能夠執(zhí)行炼彪;但是高優(yōu)先級的線程sleep(500)后,低優(yōu)先級就有機會執(zhí)行了正歼。
總之辐马,sleep()可以使低優(yōu)先級的線程得到執(zhí)行的機會,當然也可以讓同優(yōu)先級局义、高優(yōu)先級的線程有執(zhí)行的機會喜爷。yield()
該方法與sleep()類似,只是不能由用戶指定暫停多長時間萄唇。這里還需補充一點檩帐,yield()方法對應了如下操作: 先檢測當前是否有相同優(yōu)先級的線程處于同可運行狀態(tài), 如有, 則把 CPU 的占有權(quán)交給此線程, 否則繼續(xù)運行原來的線程.。所以yield()方法稱為“退讓”, 它把運行機會讓給了同等優(yōu)先級的其他線程另萤。wait()和notify()湃密、notifyAll()
這三個方法用于協(xié)調(diào)多個線程對共享數(shù)據(jù)的存取,wait()有出讓Object鎖的語義, 要想出讓鎖, 前提是要先獲得鎖, 所以要先用synchronized獲得鎖之后才能調(diào)用wait()仲墨, notify原因類似勾缭。
wait()方法使當前線程暫停執(zhí)行并釋放對象鎖標示,讓其他線程可以進入synchronized數(shù)據(jù)塊目养,當前線程被放入對象等待池中俩由。當調(diào)用notify()方法后,將從對象的等待池中移走一個任意的線程并放到鎖標志等待池中癌蚁,只有鎖標志等待池中線程能夠獲取鎖標志幻梯;如果鎖標志等待池中沒有線程,則notify()不起作用努释。
notifyAll()則從對象等待池中移走所有等待那個對象的線程并放到鎖標志等待池中碘梢。
同步鎖(synchonrize)的使用
synchronize關(guān)鍵字主要有下面5種用法
- 在方法上進行同步, 分為(1)instance method/(2)static method, 這兩個的區(qū)別后面說
- 在內(nèi)部塊上進行同步, 分為(3)synchronize(this), (4)synchonrize(XXX.class), (5)synchonrize(mutex)
代碼示例如下:
private int value = 0;
private final Object mutex = new Object();
public synchronized int incAndGet0() {
return ++value;
}
public static synchonrize int incAndGet1();
public int incAndGet2() {
synchronized(this){
return ++value;
}
}
public int incAndGet3() {
synchronized(SyncMethod.class){
return ++value;
}
}
public int incAndGet4() {
synchronized(mutex){
return ++value;
}
}
- 作為修飾符加在方法聲明上, synchronized修飾非靜態(tài)方法時表示鎖住了調(diào)用該方法的堆對象, 修飾靜態(tài)方法時表示鎖住了這個類在方法區(qū)中的類對象。
- 關(guān)于Java中的堆伐蒂、棧和方法區(qū)煞躬,請大家移步很詳細的介紹了以上內(nèi)容,感覺作為程序員真是學海無涯啊。
- synchronized(X.class) 使用類對象作為監(jiān)控恩沛。 同一時間只有一個線程可以能訪問塊中資源在扰。
- synchronized(this)和synchronized(mutex) 都是對象鎖, 同一時間每個實例都保證只能有一個實例能訪問塊中資源。
以上就是關(guān)于synchonrize關(guān)鍵字的基礎(chǔ)知識雷客。其他還有一些關(guān)于synchronized和volatile比較和synchonrize和juc中的鎖比較
這里也簡單介紹一下:
- volatile
鎖提供了兩種主要特性:互斥(mutual exclusion) 和可見性(visibility)芒珠。- 互斥,即一次只允許一個線程持有某個特定的鎖搅裙,因此可使用該特性實現(xiàn)對共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議皱卓,這樣,一次就只有一個線程能夠使用該共享數(shù)據(jù)部逮。
- 可見性娜汁,它必須確保釋放鎖之前對共享數(shù)據(jù)做出的更改對于隨后獲得該鎖的另一個線程是可見的 —— 如果沒有同步機制提供的這種可見性保證,線程看到的共享變量可能是修改前的值或不一致的值甥啄,這將引發(fā)許多嚴重問題存炮。
Volatile 變量具有 synchronized的可見性特性,Volatile是輕量級的synchronized蜈漓。
當且僅當下面條件全部滿足時, 才能使用volatile
- 對變量的寫入操作不依賴于變量的當前值, (++i/i++這種肯定不行), 或者能確保只有單個線程在更新
- 該變量不會與其他狀態(tài)變量一起納入不變性條件中
- 訪問變量時不需要加鎖
- ReentrantLock
ReentrantLock在內(nèi)存上的語義于synchronize相同, 但是它提供了額外的功能, 可以作為一種高級工具. 當需要一些 可定時, 可輪詢, 可中斷的鎖獲取操作, 或者希望使用公平鎖, 或者使用非塊結(jié)構(gòu)的編碼時 才應該考慮ReetrantLock穆桂。
以上就是這兩種鎖的比較的簡單介紹,想要深入學習的同學請大家自行搜索學習吧融虽。最后送給正在進行面試同學們的一句話:面試官虐我千百遍享完,我待面試官如初戀。---必須正能量有额,清噴般又。