1. 程式人生 > >java中執行緒阻塞之sleep、suspend、join、wait、resume、notify方法解析(一)

java中執行緒阻塞之sleep、suspend、join、wait、resume、notify方法解析(一)

java執行緒的5狀態包括create、runnable、running、blocked、dead。

create是指用new執行緒被建立,但是還沒有準備好各種資源。

runnable是指使用start啟動執行緒,這時候系統會為執行緒分配除cpu以外的所有需要的資源。

running是指cpu只會排程處於runnable狀態的執行緒使其佔用cpu時間片真正開始執行。

blocked是指由於某種原因導致running中的執行緒暫停執行,被放到阻塞佇列中,cpu不會再給他們分配時間片,直到導致阻塞的原因被解除而變為runnable狀態。

dead指執行緒結束,包括正常結束(如執行完run方法)和非正常結束(如丟擲異常等)。

那麼造成blocked的原因有哪些呢?

(1) 呼叫 sleep(毫秒數),使執行緒進入“睡眠”狀態。在規定的時間內,這個執行緒是不會執行的。
(2) 用 suspend()暫停了執行緒的執行。除非執行緒收到 resume() 訊息,否則不會返回“可執行”狀態。
(3) 用 wait()暫停了執行緒的執行。除非執行緒收到 nofify() 或者 notifyAll()訊息,否則不會變成“可執行”(是的,看起來同原因 2 非常相象,但有一個明顯區別是我們馬上要揭示的)
(4) 執行緒正在等候一些 IO(輸入輸出)操作完成。
(5) 執行緒試圖呼叫另一個物件的“同步”方法,但那個物件處於鎖定狀態,暫時無法使用。

先寫兩個基類:

class Blockable extends Thread {
	private Peeker peeker;
	protected int i;
	public Blockable(){
		peeker = new Peeker(this);
	}
	
	public synchronized int read(){ return i;}
	
	public synchronized void update(){
		System.out.println(this.getClass().getName()+"state :i = " + i);
	}
	
	public void stopPeeker(){
		peeker.terminate();
	}
}

class Peeker extends Thread {
	private Blockable b;
	private int session;
	private boolean stop = false;
	
	public Peeker(Blockable b){
		this.b = b;
		start();
	}
	
	public void run(){
		while (!stop) {
			System.out.println(b.getClass().getName()
			+ " Peeker " + (++session)
			+ "; value = " + b.read());
			try {
				sleep(1000);
			} catch (InterruptedException e){}
		}
	}
	
	public void terminate() {stop = true;}
	
}

Blockable 類打算成為本例所有類的一個基礎類。一個 Blockable 物件包含了一個i資訊。用於顯示這些資訊的方法叫作 update() 。我們發現它用
getClass.getName() 來產生類名,而不是僅僅把它打印出來;這是由於 update()不知道自己為其呼叫的那個類的準確名字,因為那個類是從 Blockable 衍生出來的。 

在 Blockable 中,變動指示符是一個 int i;衍生類的 run()方法會為其增值。
針對每個 Bloackable 物件,都會啟動 Peeker 類的一個執行緒。Peeker 的任務是呼叫 read()方法,檢查與自己
關聯的 Blockable 物件,看看 i 是否發生了變化,最後列印檢查結果。注意 read()和 update() 都是同步的,要求物件的鎖定能自由解除,這一點非常重要。

(1)sleep方法的呼叫阻塞自己時,也會引起其他執行緒阻塞是因為sleep不會釋放物件鎖。下是測試程式碼:

class Sleeper1 extends Blockable {

	public Sleeper1() {
		super();
	}
	
	public synchronized void run() {
		while(true) {
			i++;
			update();
			try {
				sleep(1000);
			} catch (InterruptedException e){}
		}
	}
}

class Sleeper2 extends Blockable {
	public Sleeper2() {
		super();
	}
	
	public void run() {
		while(true) {
			change();
			try {
				sleep(1000);
			} catch (InterruptedException e){}
		}
	}
	
	private synchronized void change(){
		i++;
		update();
	}
}
public class SleepBlock {

	public static void main(String[] args) {
		new Sleeper1().start();
		new Sleeper2().start();
	}

}
我們會發現Sleeper1中的run方法是同步的,由於run方法裡面是死迴圈,當Sleeper1執行緒啟動後,Sleeper1內的Peeker執行緒根本無法執行。因為Sleeper1的sleep方法並不會釋放物件鎖。

      在 Sleeper1 中,整個 run()方法都是同步的。我們可看到與這個物件關聯在一起的 Peeker 可以正常執行,直到我們啟動執行緒為止,隨後 Peeker 便會完全停止。這正是“堵塞”的一種形式:因為 Sleeper1.run()是同步的,而且一旦執行緒啟動,它就肯定在 run()內部,方法永遠不會放棄物件鎖定,造成 Peeker 執行緒的堵塞。
     Sleeper2 通過設定不同步的執行,提供了一種解決方案。只有 change() 方法才是同步的,所以儘管 run()位於 sleep()內部,Peeker 仍然能訪問自己需要的同步方法——read()。在這裡,我們可看到在啟動了Sleeper2 執行緒以後,Peeker 會持續執行下去。

(2)suspend方法呼叫阻塞自己時,引起其他執行緒阻塞也是因為suspend方法不會釋放物件鎖,這是不安全的。此方法已經廢棄。因為使用suspend會發生一種很愚蠢的現象,就是自己把自己掛起,也就是進入阻塞狀態。但是還搶著物件鎖(前提是有同步方法訪問同樣的資源),這樣其他執行緒來訪問同步方法也只能傻傻等待。就好像你自己拿著鑰匙在那等別人來開門,別人來開你又不給鑰匙。自己阻塞後還不讓別人獲得鎖從而執行,讓別人也跟著在那兒等。

(3)wait方法阻塞自己時會釋放物件鎖,因此不會引起其他執行緒因得不到鎖而阻塞,這一點和sleep、suspend是根本區別,因此他不會阻礙其他執行緒。

若必須等候其他某些條件(從執行緒外部加以控制)發生變化,同時又不想線上程內一直傻乎乎地等下去,一般就需要用到 wait()。wait()允許我們將執行緒置入“睡眠”狀態,同時又“積極”地等待條件發生改變。而且只有在一個 notify() 或 notifyAll()發生變化的時候,執行緒才會被喚醒,並檢查條件是否有變。因此,我們認為它提供了線上程間進行同步的一種手段。

我們也可以看到 wait()的兩種形式。第一種形式採用一個以毫秒為單位的引數,它具有與 sleep()中相同的含義:暫停這一段規定時間。區別在於在 wait()中,物件鎖已被解除,而且能夠自由地退出 wait(),因為一個 notify() 可強行使時間流逝。第二種形式不採用任何引數,這意味著 wait()會持續執行,直到 notify() 介入為止。而且在一段時間以後,不會自行中止。

(4)IO引起的阻塞。這一部分主要是IO那一塊的。由於訪問IO可能會等待,因此會引起阻塞。

(5)在上面已經可以看出來了,不同執行緒在訪問相同物件的同步方法時,會引起阻塞。需要等待一方先完成才能進行。

另外還有join()和yield()方法,

其中join的使用方式是threadname.join(),他的作用是讓當前執行緒(注意當前執行緒不是threadname,而是執行這個語句的執行緒)阻塞,等待threadname執行緒執行完之後再執行,這也是我們所說的執行緒合併,因為它相當於把本來兩條單獨的執行路線合併到一起了。


但是yeild()方法根本都不是阻塞,他是讓執行緒主動放棄cpu,等待下次排程,並沒有被放入阻塞佇列。

最後,如果一個程式執行緒很多,某些執行緒阻塞之後要想讓其儘快解除阻塞去執行,就需要儘快讓阻塞解除的條件發生。這時,需要弄清楚阻塞條件,如果有其他執行緒能加快該阻塞執行緒的阻塞解除速度,那麼可以調整其他未阻塞執行緒的優先順序,讓未阻塞執行緒儘快多的執行從而加速阻塞執行緒解除阻塞,這裡也就是動態調整執行緒優先順序的方法來提高整個系統的執行效率。如果是IO阻塞,例如網路阻塞,那麼這個時候可以從網路方面入手,如果是檔案阻塞可以從磁碟IO入手。

相關推薦

java執行阻塞sleepsuspendjoinwaitresumenotify方法解析

java執行緒的5狀態包括create、runnable、running、blocked、dead。 create是指用new執行緒被建立,但是還沒有準備好各種資源。 runnable是指使用start啟動執行緒,這時候系統會為執行緒分配除cpu以外的所有需要的資源。 ru

Java執行-併發sleep() 和 wait(n) wait() 的區別

sleep() 和 wait(n) 、 wait() 的區別 答: sleep 方法:是 Thread 類的靜態方法,當前執行緒將睡眠 n 毫秒,執行緒進入阻塞狀態。當睡眠時間到了,會接觸阻塞,進入可執行狀態,等待 CPU 的到來。睡眠不釋放鎖(如果有的話) wai

下拉重新整理上拉載入更多控制元件實現原理及解析

以前那個賬號,以後可能不用了,把文章搬過來!!! 效果預覽 接受hi大頭鬼hi的建議,來一個動態圖,方便大家知道這是個什麼東西。 動機 原理     無論是下拉重新整理還是上拉載入更多,原理都是在內容View(ListView、Re

Java執行的同步非同步以及執行的安全

  什麼是執行緒同步?     這裡的“同”是協同的意思,並不是共同。那麼就很好理解同步了,就是一起操作但並不是同時操作。比如流水線組裝汽車,可以視作一個執行緒,只有裝好了上一步的門,才可以裝下一步的門窗玻璃。沒有裝門能裝上玻璃嗎?顯然不行。所以執

Java執行學習waitnotify/notifyAll 詳解

轉載 https://www.cnblogs.com/moongeek/p/7631447.html 1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。 2、wait()使當前執行緒阻塞,前提是 必須先獲

java執行睡眠sleep()方法介紹

1、方法介紹    sleep(long millis)  執行緒睡眠 millis 毫秒    sleep(long millis, int nanos)  執行緒睡眠 millis 毫秒 + nanos 納秒2、如何呼叫sleep    因為sleep()是靜態方法,所以

Java執行學習執行執行池的使用

執行緒組 Java中使用ThreadGroup來表示執行緒組,可以對一批執行緒進行分類管理。 package thread; public class MyRunnable implements Runnable { @Override public void run() { fo

JAVA執行機制暫停恢復和停止

在JDK1.2以前的版本如果要實現執行緒的暫停、恢復和停止的方法分別是suspend()、resume()、stop()。但是從JDK1.2以後這些方法已經被遺棄,因為它們有可能造成嚴重的系統錯誤和異常。 首先suspend()方法不會釋放執行緒所佔用的資源。

Thread執行控制sleepjoinsetDaemon方法的用處

Thread執行緒控制之sleep、join、setDaemon方法的用處   1. sleep方法 public static void sleep(long millis) throws InterruptedException 使當前正在執行的

沉澱再出發:java執行解析

沉澱再出發:java中執行緒池解析 一、前言    在多執行緒執行的環境之中,如果執行緒執行的時間短但是啟動的執行緒又非常多,執行緒運轉的時間基本上浪費在了建立和銷燬上面,因此有沒有一種方式能夠讓一個執行緒執行完自己的任務之後又被重複使用呢?執行緒池的出現就是為了解決這個問題。到了現在

Java執行池基本api及其作用

1.執行緒池相關的類 2.重要類的api及其方法 Executors.newCachedThreadPool() 建立一個可快取的執行緒池 Executors.newSingleThreadExecutor();建立一個只有一個執行緒執行的 不可修改的執行緒池  

java執行程式設計使用Synchronized塊同步變數

通過synchronized塊來同步特定的靜態或非靜態方法。 要想實現這種需求必須為這些特性的方法定義一個類變數,然後將這些方法的程式碼用synchronized塊括起來,並將這個類變數作為引數傳入synchronized塊   下面的程式碼演示瞭如何同步特定的類方法:

Java執行安全的單例模式

Java中執行緒安全的單例 深入研究Servlet執行緒安全性問題 來源:網路整理 作者:2017年12月01日 14:03 0 分享 訂閱 關鍵詞:Servlet執行緒     Servlet(Server Applet)是Java Servle

java執行池的生命週期

執行緒池生命週期包括: RUNNING:接收新的任務並處理佇列中的任務 SHUTDOWN:不接收新的任務,但是處理佇列中的任務 STOP:不接收新的任務,不處理佇列中的任務,同時中斷處理中的任務 TIDYING:所有的任務處理完成,有效的執行緒數是0 TERMINATED:termin

JAVA執行問題死鎖】

  一、死鎖是什麼? 舉個例子:兩個人一起吃飯,每個人都拿了一隻筷子,雙方都在等待對方將筷子讓給自己,結果兩個人都吃不了飯。這種情況和計算機中的死鎖情況很相似。 假設有兩個執行緒,互相等待對方釋放佔有的鎖,但是釋放鎖的條件又不可能形成,這時候死鎖就形成了。 還是買票的問題,有的時候時會發生死

Java執行-併發執行

執行緒池有了解嗎? 答: java.util.concurrent.ThreadPoolExecutor 類就是一個執行緒池。客戶端呼叫ThreadPoolExecutor.submit(Runnable task) 提交任務,執行緒池內部維護的工作者執行緒的數量就是該執行緒池的執行

Java執行-併發synchronized 關鍵字

synchronized 關鍵字 答: 底層實現: 進入時,執行 monitorenter,將計數器 +1,釋放鎖 monitorexit 時,計數器 -1 當一個執行緒判斷到計數器為 0 時,則當前鎖空閒,可以佔用;反之,當前執行緒進入等待狀態 含義

Java執行-併發執行產生死鎖的4個必要條件?如何避免死鎖?

多執行緒產生死鎖的4個必要條件? 答: 互斥條件:一個資源每次只能被一個執行緒使用 請求與保持條件:一個執行緒因請求資源而阻塞時,對已獲得的資源保持不放 不剝奪條件:程序已經獲得的資源,在未使用完之前,不能強行剝奪 迴圈等待條件:若干執行緒之間形成一種頭

Java執行-併發執行和程序的區別

執行緒和程序的區別 答: 程序是一個“執行中的程式”,是系統進行資源分配和排程的一個獨立單位 執行緒是程序的一個實體,一個程序中擁有多個執行緒,執行緒之間共享地址空間和其他資源(所以通訊和同步等操作執行緒比程序更加容易) 執行緒上下文的切換比程序上下文切換要快

Java執行-併發如何制定多個執行執行順序?

文章目錄 如何讓10個執行緒按照順序列印0123456789? 程式碼如下: 1.建立一個鎖物件類 2.建立一個執行緒類 3.測試類 如何讓10個執行緒按照順序列印012