歡迎光臨
每天分享高質量文章

Java 高併發面試題

(給ImportNew加星標,提高Java技能)

 

作者:行者路上

連結:blog.csdn.net/u012998254/article/details/79400549

 

1、執行緒與行程

 

  1. 行程是一個物體。每一個行程都有它自己的地址空間,一般情況下,包括文字區域(text region)、資料區域(data region)和堆疊(stack region)。文字區域儲存處理器執行的程式碼;資料區域儲存變數和行程執行期間使用的動態分配的記憶體;堆疊區域儲存著活動過程呼叫的指令和本地變數。
  2. 一個標準的執行緒由執行緒ID,當前指令指標(PC),暫存器集合和堆疊組成。另外,執行緒是行程中的一個物體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點兒在執行中必不可少的資源,但它可與同屬一個行程的其它執行緒共享行程所擁有的全部資源。
  3. 區別不同 
  1. 地址空間:行程內的一個執行單元;行程至少有一個執行緒;它們共享行程的地址空間;而行程有自己獨立的地址空間; 
  2. 資源擁有:行程是資源分配和擁有的單位,同一個行程內的執行緒共享行程的資 
  3. 執行緒是處理器排程的基本單位,但行程不是. 
  4. 二者均可併發執行.

 

2、 守護執行緒

 

在Java中有兩類執行緒:使用者執行緒 (User Thread)、守護執行緒 (Daemon Thread)。 

守護執行緒和使用者執行緒的區別在於:守護執行緒依賴於建立它的執行緒,而使用者執行緒則不依賴。舉個簡單的例子:如果在main執行緒中建立了一個守護執行緒,當main方法執行完畢之後,守護執行緒也會隨著消亡。而使用者執行緒則不會,使用者執行緒會一直執行直到其執行完畢。在JVM中,像垃圾收集器執行緒就是守護執行緒。

 

3、java thread狀態

 

  1. NEW 狀態是指執行緒剛建立, 尚未啟動
  2. RUNNABLE 狀態是執行緒正在正常執行中, 當然可能會有某種耗時計算/IO等待的操作/CPU時間片切換等, 這個狀態下發生的等待一般是其他系統資源, 而不是鎖, Sleep等
  3. BLOCKED 這個狀態下, 是在多個執行緒有同步操作的場景, 比如正在等待另一個執行緒的synchronized 塊的執行釋放, 也就是這裡是執行緒在等待進入臨界區
  4. WAITING 這個狀態下是指執行緒擁有了某個鎖之後, 呼叫了他的wait方法, 等待其他執行緒/鎖擁有者呼叫 notify / notifyAll 一遍該執行緒可以繼續下一步操作, 這裡要區分 BLOCKED 和 WATING 的區別, 一個是在臨界點外面等待進入, 一個是在理解點裡面wait等待別人notify, 執行緒呼叫了join方法 join了另外的執行緒的時候, 也會進入WAITING狀態, 等待被他join的執行緒執行結束
  5. TIMED_WAITING 這個狀態就是有限的(時間限制)的WAITING, 一般出現在呼叫wait(long), join(long)等情況下, 另外一個執行緒sleep後, 也會進入TIMED_WAITING狀態
  6. TERMINATED 這個狀態下表示 該執行緒的run方法已經執行完畢了, 基本上就等於死亡了(當時如果執行緒被持久持有, 可能不會被回收)

 

4、請說出與執行緒同步以及執行緒排程相關的方法。

 

  1. wait():使一個執行緒處於等待(阻塞)狀態,並且釋放所持有的物件的鎖;
  2. sleep():使一個正在執行的執行緒處於睡眠狀態,是一個靜態方法,呼叫此方法要處理InterruptedException異常;
  3. notify():喚醒一個處於等待狀態的執行緒,當然在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且與優先順序無關;
  4. notityAll():喚醒所有處於等待狀態的執行緒,該方法並不是將物件的鎖給所有執行緒,而是讓它們競爭,只有獲得鎖的執行緒才能進入就緒狀態;

 

5、行程排程演演算法

 

實時系統:FIFO(First Input First Output,先進先出演演算法),SJF(Shortest Job First,最短作業優先演演算法),SRTF(Shortest Remaining Time First,最短剩餘時間優先演演算法)。 

 

互動式系統:RR(Round Robin,時間片輪轉演演算法),HPF(Highest Priority First,最高優先順序演演算法),多級佇列,最短行程優先,保證排程,彩票排程,公平分享排程。

 

6、wait()和sleep()的區別

 

  1. sleep來自Thread類,和wait來自Object類
  2. 呼叫sleep()方法的過程中,執行緒不會釋放物件鎖。而 呼叫 wait 方法執行緒會釋放物件鎖
  3. sleep睡眠後不出讓系統資源,wait讓出系統資源其他執行緒可以佔用CPU
  4. sleep(milliseconds)需要指定一個睡眠時間,時間一到會自動喚醒

 

7、ThreadLocal,以及死鎖分析

 

hreadLocal為每個執行緒維護一個本地變數。 

 

採用空間換時間,它用於執行緒間的資料隔離,為每一個使用該變數的執行緒提供一個副本,每個執行緒都可以獨立地改變自己的副本,而不會和其他執行緒的副本衝突。 

 

ThreadLocal類中維護一個Map,用於儲存每一個執行緒的變數副本,Map中元素的鍵為執行緒物件,而值為對應執行緒的變數副本。 

 

8、Synchronized 與Lock

 

  1. ReentrantLock 擁有Synchronized相同的併發性和記憶體語意,此外還多了 鎖投票,定時鎖等候和中斷鎖等候 ,執行緒A和B都要獲取物件O的鎖定,假設A獲取了物件O鎖,B將等待A釋放對O的鎖定, 如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷 ,如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而乾別的事情
  2. ReentrantLock獲取鎖定與三種方式: 
  1. lock(), 如果獲取了鎖立即傳回,如果別的執行緒持有鎖,當前執行緒則一直處於休眠狀態,直到獲取鎖 
  2. tryLock(), 如果獲取了鎖立即傳回true,如果別的執行緒正持有鎖,立即傳回false; 
  3. tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即傳回true,如果別的執行緒正持有鎖,會等待引數給定的時間,在等待的過程中,如果獲取了鎖定,就傳回true,如果等待超時,傳回false; 
  4. lockInterruptibly:如果獲取了鎖定立即傳回,如果沒有獲取鎖定,當前執行緒處於休眠狀態,直到或者鎖定,或者當前執行緒被別的執行緒中斷

 

總體的結論先擺出來:

 

synchronized: 

在資源競爭不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合適的。原因在於,編譯程式通常會盡可能的進行最佳化synchronized,另外可讀性非常好,不管用沒用過5.0多執行緒包的程式員都能理解。 

 

ReentrantLock

ReentrantLock提供了多樣化的同步,比如有時間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競爭不激烈的情形下,效能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的效能一下子能下降好幾十倍。而ReentrantLock確還能維持常態。

 

9、Volatile和Synchronized

 

Volatile和Synchronized四個不同點:

 

  1. 粒度不同,前者針對變數 ,後者鎖物件和類
  2. syn阻塞,volatile執行緒不阻塞
  3. syn保證三大特性,volatile不保證原子性
  4. syn編譯器最佳化,volatile不最佳化 

要使 volatile 變數提供理想的執行緒安全,必須同時滿足下麵兩個條件: 

  1. 對變數的寫操作不依賴於當前值。
  2. 該變數沒有包含在具有其他變數的不變式中。

 

10、CAS

 

CAS是樂觀鎖技術,當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值,而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。CAS有3個運算元,記憶體值V,舊的預期值A,要修改的新值B。當且僅當預期值A和記憶體值V相同時,將記憶體值V修改為B,否則什麼都不做。

 

11、Java中Unsafe類詳解

 

  1. 透過Unsafe類可以分配記憶體,可以釋放記憶體;類中提供的3個本地方法allocateMemory、reallocateMemory、freeMemory分別用於分配記憶體,擴充記憶體和釋放記憶體,與C語言中的3個方法對應。
  2. 可以定位物件某欄位的記憶體位置,也可以修改物件的欄位值,即使它是私有的;
  3. 掛起與恢復:將一個執行緒進行掛起是透過park方法實現的,呼叫 park後,執行緒將一直阻塞直到超時或者中斷等條件出現。unpark可以終止一個掛起的執行緒,使其恢復正常。整個併發框架中對執行緒的掛起操作被封裝在 LockSupport類中,LockSupport類中有各種版本pack方法,但最終都呼叫了Unsafe.park()方法。
  4. cas 

 

12、執行緒池

 

執行緒池的作用: 

 

在程式啟動的時候就建立若干執行緒來響應處理,它們被稱為執行緒池,裡面的執行緒叫工作執行緒 

 

第一:降低資源消耗。透過重覆利用已建立的執行緒降低執行緒建立和銷毀造成的消耗。 

 

第二:提高響應速度。當任務到達時,任務可以不需要等到執行緒建立就能立即執行。 

 

第三:提高執行緒的可管理性。 

 

常用執行緒池:ExecutorService 是主要的實現類,其中常用的有 

Executors.newSingleT 

hreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

 

13、ThreadPoolExecutor

 

構造方法引數說明

 

corePoolSize:核心執行緒數,預設情況下核心執行緒會一直存活,即使處於閑置狀態也不會受存keepAliveTime限制。除非將allowCoreThreadTimeOut設定為true。

 

maximumPoolSize:執行緒池所能容納的最大執行緒數。超過這個數的執行緒將被阻塞。當任務佇列為沒有設定大小的LinkedBlockingDeque時,這個值無效。 

 

keepAliveTime:非核心執行緒的閑置超時時間,超過這個時間就會被回收。 

 

unit:指定keepAliveTime的單位,如TimeUnit.SECONDS。當將allowCoreThreadTimeOut設定為true時對corePoolSize生效。 

 

workQueue:執行緒池中的任務佇列. 

常用的有三種佇列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。

 

threadFactory:執行緒工廠,提供建立新執行緒的功能。ThreadFactory是一個介面,只有一個方法

 

原理

 

  1. 如果當前池大小 poolSize 小於 corePoolSize ,則建立新執行緒執行任務。
  2. 如果當前池大小 poolSize 大於 corePoolSize ,且等待佇列未滿,則進入等待佇列
  3. 如果當前池大小 poolSize 大於 corePoolSize 且小於 maximumPoolSize ,且等待佇列已滿,則建立新執行緒執行任務。
  4. 如果當前池大小 poolSize 大於 corePoolSize 且大於 maximumPoolSize ,且等待佇列已滿,則呼叫拒絕策略來處理該任務。
  5. 執行緒池裡的每個執行緒執行完任務後不會立刻退出,而是會去檢查下等待佇列裡是否還有執行緒任務需要執行,如果在 keepAliveTime 裡等不到新的任務了,那麼執行緒就會退出。

 

13、Executor拒絕策略

 

  1. AbortPolicy:為java執行緒池預設的阻塞策略,不執行此任務,而且直接丟擲一個執行時異常,切記ThreadPoolExecutor.execute需要try 

    catch,否則程式會直接退出.

  2. DiscardPolicy:直接拋棄,任務不執行,空方法
  3. DiscardOldestPolicy:從佇列裡面拋棄head的一個任務,並再次execute 此task。
  4. CallerRunsPolicy:在呼叫execute的執行緒裡面執行此command,會阻塞入
  5. 使用者自定義拒絕策略:實現RejectedExecutionHandler,並自己定義策略樣式

 

14、CachedThreadPool 、 FixedThreadPool、SingleThreadPool

 

  1. newSingleThreadExecutor :建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務, 保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行 

    適用場景:任務少 ,並且不需要併發執行

  2. newCachedThreadPool :建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閑執行緒,若無可回收,則新建執行緒. 

    執行緒沒有任務要執行時,便處於空閑狀態,處於空閑狀態的執行緒並不會被立即銷毀(會被快取住),只有當空閑時間超出一段時間(預設為60s)後,執行緒池才會銷毀該執行緒(相當於清除過時的快取)。新任務到達後,執行緒池首先會讓被快取住的執行緒(空閑狀態)去執行任務,如果沒有可用執行緒(無空閑執行緒),便會建立新的執行緒。 

    適用場景:處理任務速度 > 提交任務速度,耗時少的任務(避免無限新增執行緒)

  3. newFixedThreadPool :建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
  4. newScheduledThreadPool:建立一個定長執行緒池,支援定時及週期性任務執行

 

15、CopyOnWriteArrayList

 

CopyOnWriteArrayList : 寫時加鎖,當新增一個元素的時候,將原來的容器進行copy,複製出一個新的容器,然後在新的容器裡面寫,寫完之後再將原容器的取用指向新的容器,而讀的時候是讀舊容器的資料,所以可以進行併發的讀,但這是一種弱一致性的策略。 

 

使用場景:CopyOnWriteArrayList適合使用在讀操作遠遠大於寫操作的場景裡,比如快取。

 

16、AQS

 

AQS使用一個int成員變數來表示同步狀態,透過內建的FIFO佇列來完成獲取資源執行緒的排隊工作。

 

private volatile int state;//共享變數,使用volatile修飾保證執行緒可見性

 

  1. 2種同步方式:獨佔式,共享式。獨佔式如ReentrantLock,共享式如Semaphore,CountDownLatch,組合式的如ReentrantReadWriteLock
  2. 節點的狀態 

    CANCELLED,值為1,表示當前的執行緒被取消; 

    SIGNAL,值為-1,表示當前節點的後繼節點包含的執行緒需要執行,也就是unpark; 

    CONDITION,值為-2,表示當前節點在等待condition,也就是在condition佇列中; 

    PROPAGATE,值為-3,表示當前場景下後續的acquireShared能夠得以執行; 

    值為0,表示當前節點在sync佇列中,等待著獲取鎖。

  3. 模板方法樣式 

    protected boolean tryAcquire(int arg) : 獨佔式獲取同步狀態,試著獲取,成功傳回true,反之為false 

    protected boolean tryRelease(int arg) :獨佔式釋放同步狀態,等待中的其他執行緒此時將有機會獲取到同步狀態; 

    protected int tryAcquireShared(int arg) :共享式獲取同步狀態,傳回值大於等於0,代表獲取成功;反之獲取失敗; 

    protected boolean tryReleaseShared(int arg) :共享式釋放同步狀態,成功為true,失敗為false 

    AQS維護一個共享資源state,透過內建的FIFO來完成獲取資源執行緒的排隊工作。該佇列由一個一個的Node結點組成,每個Node結點維護一個prev取用和next取用,分別指向自己的前驅和後繼結點。雙端雙向連結串列。 

     

     

    1.獨佔式:樂觀的併發策略 

    acquire 

    a.首先tryAcquire獲取同步狀態,成功則直接傳回;否則,進入下一環節; 

    b.執行緒獲取同步狀態失敗,就構造一個結點,加入同步佇列中,這個過程要保證執行緒安全; 

    c.加入佇列中的結點執行緒進入自旋狀態,若是老二結點(即前驅結點為頭結點),才有機會嘗試去獲取同步狀態;否則,當其前驅結點的狀態為SIGNAL,執行緒便可安心休息,進入阻塞狀態,直到被中斷或者被前驅結點喚醒。 

    release 

    release的同步狀態相對簡單,需要找到頭結點的後繼結點進行喚醒,若後繼結點為空或處於CANCEL狀態,從後向前遍歷找尋一個正常的結點,喚醒其對應執行緒。

  4. 共享式: 

    共享式地獲取同步狀態.同步狀態的方法tryAcquireShared傳回值為int。 

    a.當傳回值大於0時,表示獲取同步狀態成功,同時還有剩餘同步狀態可供其他執行緒獲取; 

    b.當傳回值等於0時,表示獲取同步狀態成功,但沒有可用同步狀態了; 

    c.當傳回值小於0時,表示獲取同步狀態失敗。

  5. AQS實現公平鎖和非公平鎖 

    非公平鎖中,那些嘗試獲取鎖且尚未進入等待佇列的執行緒會和等待佇列head結點的執行緒發生競爭。公平鎖中,在獲取鎖時,增加了isFirst(current)判斷,當且僅當,等待佇列為空或當前執行緒是等待佇列的頭結點時,才可嘗試獲取鎖。 

 

16、Java裡的阻塞佇列

 

7個阻塞佇列。分別是

 

  1. ArrayBlockingQueue :一個由陣列結構組成的有界阻塞佇列。 
  2. LinkedBlockingQueue :一個由連結串列結構組成的有界阻塞佇列。 
  3. PriorityBlockingQueue :一個支援優先順序排序的無界阻塞佇列。 
  4. DelayQueue:一個使用優先順序佇列實現的無界阻塞佇列。 
  5. SynchronousQueue:一個不儲存元素的阻塞佇列。 
  6. LinkedTransferQueue:一個由連結串列結構組成的無界阻塞佇列。 
  7. LinkedBlockingDeque:一個由連結串列結構組成的雙向阻塞佇列。

 

新增元素

 

Java中的阻塞佇列介面BlockingQueue繼承自Queue介面。BlockingQueue介面提供了3個新增元素方法。

 

  • add:新增元素到佇列裡,新增成功傳回true,由於容量滿了新增失敗會丟擲IllegalStateException異常 
  • offer:新增元素到佇列裡,新增成功傳回true,新增失敗傳回false 
  • put:新增元素到佇列裡,如果容量滿了會阻塞直到容量不滿

 

刪除方法

 

3個刪除方法 

 

  • poll:刪除佇列頭部元素,如果佇列為空,傳回null。否則傳回元素。 
  • remove:基於物件找到對應的元素,並刪除。刪除成功傳回true,否則傳回false 
  • take:刪除佇列頭部元素,如果佇列為空,一直阻塞到佇列有元素並刪除

 

17、condition

 

對Condition的原始碼理解,主要就是理解等待佇列,等待佇列可以類比同步佇列,而且等待佇列比同步佇列要簡單,因為等待佇列是單向佇列,同步佇列是雙向佇列。

 

18、DelayQueue

 

佇列中每個元素都有個過期時間,並且佇列是個優先順序佇列,當從佇列獲取元素時候,只有過期元素才會出佇列。

 

19、Fork/Join框架

 

Fork/Join框架是Java 7提供的一個用於並行執行任務的框架,是一個把大任務分割成若干個小任務,最終彙總每個小任務結果後得到大任務結果的框架。Fork/Join框架要完成兩件事情:

 

  1. 任務分割:首先Fork/Join框架需要把大的任務分割成足夠小的子任務,如果子任務比較大的話還要對子任務進行繼續分割
  2. 執行任務併合並結果:分割的子任務分別放到雙端佇列裡,然後幾個啟動執行緒分別從雙端佇列裡獲取任務執行。子任務執行完的結果都放在另外一個佇列裡,啟動一個執行緒從佇列裡取資料,然後合併這些資料。

 

在Java的Fork/Join框架中,使用兩個類完成上述操作

 

  1. ForkJoinTask:我們要使用Fork/Join框架,首先需要建立一個ForkJoin任務。該類提供了在任務中執行fork和join的機制。通常情況下我們不需要直接整合ForkJoinTask類,只需要繼承它的子類,Fork/Join框架提供了兩個子類:

             a.RecursiveAction:用於沒有傳回結果的任務

             b.RecursiveTask:用於有傳回結果的任務

  2. ForkJoinPool:ForkJoinTask需要透過ForkJoinPool來執行

 

任務分割出的子任務會新增到當前工作執行緒所維護的雙端佇列中,進入佇列的頭部。當一個工作執行緒的佇列裡暫時沒有任務時,它會隨機從其他工作執行緒的佇列的尾部獲取一個任務(工作竊取演演算法)。 

 

Fork/Join框架的實現原理 

 

ForkJoinPool由ForkJoinTask陣列和ForkJoinWorkerThread陣列組成,ForkJoinTask陣列負責將存放程式提交給ForkJoinPool,而ForkJoinWorkerThread負責執行這

 

20、原子操作類

 

在java.util.concurrent.atomic包下,可以分為四種型別的原子更新類:原子更新基本型別、原子更新陣列型別、原子更新取用和原子更新屬性。

 

  1. 原子更新基本型別 

    使用原子方式更新基本型別,共包括3個類: 

    AtomicBoolean:原子更新布林變數 

    AtomicInteger:原子更新整型變數 

    AtomicLong:原子更新長整型變數

  2. 原子更新陣列 

    透過原子更新陣列裡的某個元素,共有3個類: 

    AtomicIntegerArray:原子更新整型陣列的某個元素 

    AtomicLongArray:原子更新長整型陣列的某個元素 

    AtomicReferenceArray:原子更新取用型別陣列的某個元素 

    AtomicIntegerArray常用的方法有: 

    int addAndSet(int i, int delta):以原子方式將輸入值與陣列中索引為i的元素相加 

    boolean compareAndSet(int i, int expect, int update):如果當前值等於預期值,則以原子方式更新陣列中索引為i的值為update值

  3. 原子更新取用型別 

    AtomicReference:原子更新取用型別 

    AtomicReferenceFieldUpdater:原子更新取用型別裡的欄位 

    AtomicMarkableReference:原子更新帶有標記位的取用型別。

  4. 原子更新欄位類 

    如果需要原子更新某個類的某個欄位,就需要用到原子更新欄位類,可以使用以下幾個類: 

    AtomicIntegerFieldUpdater:原子更新整型欄位 

    AtomicLongFieldUpdater:原子更新長整型欄位 

    AtomicStampedReference:原子更新帶有版本號的取用型別。 

    要想原子更新欄位,需要兩個步驟: 

    每次必須使用newUpdater建立一個更新器,並且需要設定想要更新的類的欄位 

    更新類的欄位(屬性)必須為public volatile

 

21、同步屏障CyclicBarrier

 

CyclicBarrier 的字面意思是可迴圈使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續幹活。CyclicBarrier預設的構造方法是CyclicBarrier(int parties),其引數表示屏障攔截的執行緒數量,每個執行緒呼叫await方法告訴CyclicBarrier我已經到達了屏障,然後當前執行緒被阻塞。

 

CyclicBarrier和CountDownLatch的區別

 

CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重置。所以CyclicBarrier能處理更為複雜的業務場景,比如如果計算發生錯誤,可以重置計數器,並讓執行緒們重新執行一次。 

 

CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的執行緒數量。isBroken方法用來知道阻塞的執行緒是否被中斷。比如以下程式碼執行完之後會傳回true。

 

22、Semaphore

 

Semaphore(訊號量)是用來控制同時訪問特定資源的執行緒數量,它透過協調各個執行緒,以保證合理的使用公共資源 

 

Semaphore可以用於做流量控制,特別公用資源有限的應用場景,比如資料庫連線。假如有一個需求,要讀取幾萬個檔案的資料,因為都是IO密集型任務,我們可以啟動幾十個執行緒併發的讀取,但是如果讀到記憶體後,還需要儲存到資料庫中,而資料庫的連線數只有10個,這時我們必須控制只有十個執行緒同時獲取資料庫連線儲存資料,否則會報錯無法獲取資料庫連線。這個時候,我們就可以使用Semaphore來做流控.

 

23、死鎖,以及解決死鎖

 

死鎖產生的四個必要條件

 

互斥條件:資源是獨佔的且排他使用,行程互斥使用資源,即任意時刻一個資源只能給一個行程使用,其他行程若申請一個資源,而該資源被另一行程佔有時,則申請者等待直到資源被佔有者釋放。 

 

不可剝奪條件:行程所獲得的資源在未使用完畢之前,不被其他行程強行剝奪,而只能由獲得該資源的行程資源釋放。 

 

請求和保持條件:行程每次申請它所需要的一部分資源,在申請新的資源的同時,繼續佔用已分配到的資源。 

 

迴圈等待條件:在發生死鎖時必然存在一個行程等待佇列{P1,P2,…,Pn},其中P1等待P2佔有的資源,P2等待P3佔有的資源,…,Pn等待P1佔有的資源,形成一個行程等待環路,環路中每一個行程所佔有的資源同時被另一個申請,也就是前一個行程佔有後一個行程所深情地資源。

 

解決死鎖

 

  1. 死鎖預防,就是不讓上面的四個條件同時成立。 
  2. 合理分配資源。 
  3. 使用銀行家演演算法,如果該行程請求的資源作業系統剩餘量可以滿足,那麼就分配。

 

24、行程間的通訊方式

 

  1. 管道( pipe):管道是一種半雙工的通訊方式,資料只能單向流動,而且只能在具有親緣關係的行程間使用。行程的親緣關係通常是指父子行程關係。
  2. 有名管道 (named pipe) : 有名管道也是半雙工的通訊方式,但是它允許無親緣關係行程間的通訊。
  3. 訊號量( semophore ) : 訊號量是一個計數器,可以用來控制多個行程對共享資源的訪問。它常作為一種鎖機制,防止某行程正在訪問共享資源時,其他行程也訪問該資源。因此,主要作為行程間以及同一行程內不同執行緒之間的同步手段。
  4. 訊息佇列( message queue ) : 訊息佇列是由訊息的連結串列,存放在核心中並由訊息佇列識別符號標識。訊息佇列剋服了訊號傳遞資訊少、管道只能承載無格式位元組流以及緩衝區大小受限等缺點。
  5. 訊號 ( sinal ) : 訊號是一種比較複雜的通訊方式,用於通知接收行程某個事件已經發生。
  6. 共享記憶體( shared memory ) :共享記憶體就是對映一段能被其他行程所訪問的記憶體,這段共享記憶體由一個行程建立,但多個行程都可以訪問。共享記憶體是最快的 IPC 方式,它是針對其他行程間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號量,配合使用,來實現行程間的同步和通訊。
  7. 套接字( socket ) : 套解口也是一種行程間通訊機制,與其他通訊機制不同的是,它可用於不同機器間的行程通訊。

 

中斷

 

interrupt()的作用是中斷本執行緒。 

 

本執行緒中斷自己是被允許的;其它執行緒呼叫本執行緒的interrupt()方法時,會透過checkAccess()檢查許可權。這有可能丟擲SecurityException異常。 

 

如果本執行緒是處於阻塞狀態:呼叫執行緒的wait(), wait(long)或wait(long, int)會讓它進入等待(阻塞)狀態,或者呼叫執行緒的join(), join(long), join(long, int), sleep(long), sleep(long, int)也會讓它進入阻塞狀態。若執行緒在阻塞狀態時,呼叫了它的interrupt()方法,那麼它的“中斷狀態”會被清除並且會收到一個InterruptedException異常。例如,執行緒透過wait()進入阻塞狀態,此時透過interrupt()中斷該執行緒;呼叫interrupt()會立即將執行緒的中斷標記設為“true”,但是由於執行緒處於阻塞狀態,所以該“中斷標記”會立即被清除為“false”,同時,會產生一個InterruptedException的異常。

 

如果執行緒被阻塞在一個Selector選擇器中,那麼透過interrupt()中斷它時;執行緒的中斷標記會被設定為true,並且它會立即從選擇操作中傳回。

 

如果不屬於前面所說的情況,那麼透過interrupt()中斷執行緒時,它的中斷標記會被設定為“true”。 

 

中斷一個“已終止的執行緒”不會產生任何操作。

 

  1. 終止處於“阻塞狀態”的執行緒 

    通常,我們透過“中斷”方式終止處於“阻塞狀態”的執行緒。 

    當執行緒由於被呼叫了sleep(), wait(), join()等方法而進入阻塞狀態;若此時呼叫執行緒的interrupt()將執行緒的中斷標記設為true。由於處於阻塞狀態,中斷標記會被清除,同時產生一個InterruptedException異常。將InterruptedException放在適當的為止就能終止執行緒, 

  2. 終止處於“執行狀態”的執行緒

 

interrupted() 和 isInterrupted()的區別

 

最後談談 interrupted() 和 isInterrupted()。 

 

interrupted() 和 isInterrupted()都能夠用於檢測物件的“中斷標記”。 

 

區別是,interrupted()除了傳回中斷標記之外,它還會清除中斷標記(即將中斷標記設為false);而isInterrupted()僅僅傳回中斷標記。 

已同步到看一看
贊(0)

分享創造快樂