Java面試題沖刺第二十五天--并發(fā)編程2
面試題1:簡單說下你對線程和進(jìn)程的理解?
正經(jīng)回答:
進(jìn)程
- 一個(gè)在內(nèi)存中運(yùn)行的應(yīng)用程序。每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程可以有多個(gè)線程,比如在Windows系統(tǒng)中,一個(gè)運(yùn)行的xx.exe就是一個(gè)進(jìn)程。
線程
- 進(jìn)程中的一個(gè)執(zhí)行任務(wù)(控制單元),負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行。一個(gè)進(jìn)程至少有一個(gè)線程,一個(gè)進(jìn)程可以運(yùn)行多個(gè)線程,多個(gè)線程可共享數(shù)據(jù)。
深入追問:
追問1:那進(jìn)程和線程有哪些區(qū)別呢?
想起了某乎上的經(jīng)典回答:
做個(gè)簡單的比喻:進(jìn)程 → 火車,線程 → 車廂;線程在進(jìn)程下行進(jìn)(單純的車廂無法運(yùn)行)
- 一個(gè)進(jìn)程可以包含多個(gè)線程(一輛火車可以有多個(gè)車廂)
- 不同進(jìn)程間數(shù)據(jù)很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點(diǎn)換乘)
- 同一進(jìn)程下不同線程間數(shù)據(jù)很易共享(A車廂換到B車廂很容易)
- 進(jìn)程要比線程消耗更多的計(jì)算機(jī)資源(采用多列火車相比多個(gè)車廂更耗資源)
- 進(jìn)程間不會相互影響,一個(gè)線程掛掉將導(dǎo)致整個(gè)進(jìn)程掛掉(一列火車不會影響到另外一列火車,但是如果一列火車上中間的一節(jié)車廂著火了,將影響到所有車廂)
- 進(jìn)程可以拓展到多機(jī),線程最多擴(kuò)展到多核CPU,而不能擴(kuò)展到多機(jī)(不同火車可以開在多個(gè)軌道上,同一火車的車廂不能在行進(jìn)的不同的軌道上)
- 進(jìn)程使用的內(nèi)存地址可以上鎖,即一個(gè)線程使用某些共享內(nèi)存時(shí),其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存。(比如火車上的洗手間)-“互斥鎖”
- 進(jìn)程使用的內(nèi)存地址可以限定使用量(比如火車上的餐廳,最多只允許多少人進(jìn)入,如果滿了需要在門口等,等有人出來了才能進(jìn)去)-“信號量”
面試題2:守護(hù)線程和用戶線程的區(qū)別? 正經(jīng)回答:
用戶 (User) 線程:
- 運(yùn)行在前臺,執(zhí)行具體的任務(wù),如程序的主線程、連接網(wǎng)絡(luò)的子線程等都是用戶線程
守護(hù) (Daemon) 線程:
- 運(yùn)行在后臺,為其他前臺線程(非守護(hù)線程)服務(wù)。當(dāng)所有用戶線程都結(jié)束運(yùn)行時(shí),守護(hù)線程會隨 JVM 一起結(jié)束工作.
可見,守護(hù)線程是依賴于用戶線程,當(dāng)所有用戶線程都退出了,守護(hù)線程也就會退出,典型的守護(hù)線程如垃圾回收線程。
而用戶線程是獨(dú)立存在的,不會因?yàn)槠渌脩艟€程退出而退出。
注意事項(xiàng):
- setDaemon(true)必須在start()方法前執(zhí)行,否則會拋出 IllegalThreadStateException 異常
- 在守護(hù)線程中產(chǎn)生的新線程也是守護(hù)線程
- 不是所有的任務(wù)都可以分配給守護(hù)線程來執(zhí)行,比如讀寫操作或者計(jì)算邏輯
- 守護(hù) (Daemon) 線程中不能依靠 finally 塊的內(nèi)容來確保執(zhí)行關(guān)閉或清理資源的邏輯。因?yàn)槲覀兩厦嬉舱f過了一旦所有用戶線程都結(jié)束運(yùn)行,守護(hù)線程會隨 JVM 一起結(jié)束工作,所以守護(hù) (Daemon) 線程中的 finally 語句塊可能無法被執(zhí)行。
面試題3:什么是線程死鎖?
正經(jīng)回答:
死鎖是指兩個(gè)或兩個(gè)以上的進(jìn)程(線程)在執(zhí)行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖,這些永遠(yuǎn)在互相等待的進(jìn)程(線程)稱為死鎖進(jìn)程(線程)。
例如,在某個(gè)計(jì)算機(jī)系統(tǒng)中只有一臺打印機(jī)和一臺輸入 設(shè)備,進(jìn)程A正占用輸入設(shè)備,同時(shí)又提出使用打印機(jī)的請求,但此時(shí)打印機(jī)正被進(jìn)程B 所占用,而B在未釋放打印機(jī)之前,又提出請求使用正被A占用著的輸入設(shè)備。這樣兩個(gè)進(jìn)程相互無休止地等待下去,均無法繼續(xù)執(zhí)行,此時(shí)兩個(gè)進(jìn)程陷入死鎖狀態(tài)。
多個(gè)線程同時(shí)被阻塞,它們中的一個(gè)或者全部都在等待某個(gè)資源被釋放。由于線程被無限期地阻塞,因此程序不可能正常終止。
如下圖所示,線程 A 持有資源 2,線程 B 持有資源 1,他們同時(shí)都想申請對方的資源,所以這兩個(gè)線程就會互相等待而進(jìn)入死鎖狀態(tài)。
下面是一個(gè)死鎖示例代碼:
// 示例來自《并發(fā)編程之美》 public class DeadLockDemo { private static Object resource1 = new Object();//資源 1 private static Object resource2 = new Object();//資源 2 public static void main(String[] args) { new Thread(() -> { synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource2"); synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); } } }, "線程 1").start(); new Thread(() -> { synchronized (resource2) { System.out.println(Thread.currentThread() + "get resource2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread() + "waiting get resource1"); synchronized (resource1) { System.out.println(Thread.currentThread() + "get resource1"); } } }, "線程 2").start(); } }
打印輸出:
Thread[線程 1,5,main]get resource1
Thread[線程 2,5,main]get resource2
Thread[線程 1,5,main]waiting get resource2
Thread[線程 2,5,main]waiting get resource1
線程 A 通過 synchronized (resource1) 獲得 resource1 的監(jiān)視器鎖,然后通過Thread.sleep(1000);讓線程 A 休眠 1s 為的是讓線程 B 得到CPU執(zhí)行權(quán),從而獲取到 resource2 的監(jiān)視器鎖。
線程 A 和線程 B 休眠結(jié)束了都開始企圖請求獲取對方的資源,然后這兩個(gè)線程就會陷入互相等待的狀態(tài),這也就產(chǎn)生了死鎖。上面的例子符合產(chǎn)生死鎖的四個(gè)必要條件。
深入追問:
追問1:形成死鎖的四個(gè)必要條件是什么?
- 互斥: 某種資源一次只允許一個(gè)進(jìn)程訪問,即該資源一旦分配給某個(gè)進(jìn)程,其他進(jìn)程就不能再訪問,直到該進(jìn)程訪問結(jié)束。
- 占有且等待: 一個(gè)進(jìn)程本身占有資源(一種或多種),同時(shí)還有資源未得到滿足,正在等待其他進(jìn)程釋放該資源。
- 不可搶占: 別人已經(jīng)占有了某項(xiàng)資源,你不能因?yàn)樽约阂残枰撡Y源,就去把別人的資源搶過來。
- 循環(huán)等待: 存在一個(gè)進(jìn)程鏈,使得每個(gè)進(jìn)程都占有下一個(gè)進(jìn)程所需的至少一種資源。
當(dāng)以上四個(gè)條件均滿足,必然會造成死鎖,相反,而只要上述條件之一不滿足,就不會發(fā)生死鎖。
發(fā)生死鎖的進(jìn)程無法進(jìn)行下去,它們所持有的資源也無法釋放。這樣會導(dǎo)致CPU的吞吐量下降。所以死鎖情況是會浪費(fèi)系統(tǒng)資源和影響計(jì)算機(jī)的使用性能的。
追問2:我們該如何避免死鎖?
死鎖避免的基本思想:系統(tǒng)對進(jìn)程發(fā)出每一個(gè)系統(tǒng)能夠滿足的資源申請進(jìn)行動(dòng)態(tài)檢查,并根據(jù)檢查結(jié)果決定是否分配資源,如果分配后系統(tǒng)可能發(fā)生死鎖,則不予分配,否則予以分配。這是一種保證系統(tǒng)不進(jìn)入死鎖狀態(tài)的動(dòng)態(tài)策略。
理解了死鎖的原因,尤其是產(chǎn)生死鎖的四個(gè)必要條件,就可以最大可能地避免、預(yù)防和解除死鎖。只要打破四個(gè)必要條件之一就能有效預(yù)防死鎖的發(fā)生:
- 打破互斥條件:改造獨(dú)占性資源為虛擬資源,大部分資源已無法改造。
- 打破不可搶占條件:當(dāng)一進(jìn)程占有一獨(dú)占性資源后又申請一獨(dú)占性資源而無法滿足,則退出原占有的資源。
- 打破占有且等待條件:采用資源預(yù)先分配策略,即進(jìn)程運(yùn)行前申請全部資源,滿足則運(yùn)行,不然就等待,這樣就不會占有且申請。
- 打破循環(huán)等待條件:實(shí)現(xiàn)資源有序分配策略,對所有設(shè)備實(shí)現(xiàn)分類編號,所有進(jìn)程只能采用按序號遞增的形式申請資源。
追問3:死鎖避免和死鎖預(yù)防有啥不同?
死鎖預(yù)防是設(shè)法至少破壞產(chǎn)生死鎖的四個(gè)必要條件之一,嚴(yán)格的防止死鎖的出現(xiàn);而死鎖避免則不那么嚴(yán)格的限制產(chǎn)生死鎖的必要條件的存在,因?yàn)榧词顾梨i的必要條件存在,也不一定發(fā)生死鎖。死鎖避免是在系統(tǒng)運(yùn)行過程中注意避免死鎖的最終發(fā)生。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
初探Spring Cloud Gateway實(shí)戰(zhàn)
這篇文章主要介紹了創(chuàng)建網(wǎng)關(guān)項(xiàng)目(Spring Cloud Gateway)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-08-08Java詳細(xì)講解不同版本的接口語法和抽象類與接口的區(qū)別
對于面向?qū)ο缶幊虂碚f,抽象是它的一大特征之一,在?Java?中可以通過兩種形式來體現(xiàn)OOP的抽象:接口和抽象類,下面這篇文章主要給大家介紹了關(guān)于Java入門基礎(chǔ)之抽象類與接口的相關(guān)資料,需要的朋友可以參考下2022-04-04Spring Security 表單登錄功能的實(shí)現(xiàn)方法
這篇文章主要介紹了Spring Security 表單登錄,本文將構(gòu)建在之前簡單的 Spring MVC示例 之上,因?yàn)檫@是設(shè)置Web應(yīng)用程序和登錄機(jī)制的必不可少的。需要的朋友可以參考下2019-06-06java.lang.Instrument 代理Agent使用詳細(xì)介紹
這篇文章主要介紹了java.lang.Instrument 代理Agent使用詳細(xì)介紹的相關(guān)資料,附有實(shí)例代碼,幫助大家學(xué)習(xí)參考,需要的朋友可以參考下2016-11-11老生常談Java虛擬機(jī)垃圾回收機(jī)制(必看篇)
下面小編就為大家?guī)硪黄仙U凧ava虛擬機(jī)垃圾回收機(jī)制(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08基于JavaSwing+mysql開發(fā)一個(gè)學(xué)生社團(tuán)管理系統(tǒng)設(shè)計(jì)和實(shí)現(xiàn)
項(xiàng)目使用Java swing+mysql開發(fā),可實(shí)現(xiàn)基礎(chǔ)數(shù)據(jù)維護(hù)、用戶登錄注冊、社團(tuán)信息列表查看、社團(tuán)信息添加、社團(tuán)信息修改、社團(tuán)信息刪除以及退出注銷等功能、界面設(shè)計(jì)比較簡單易學(xué)、適合作為Java課設(shè)設(shè)計(jì)以及學(xué)習(xí)技術(shù)使用,需要的朋友參考下吧2021-08-08