首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

執行緒阻塞的五大原因與 Blocking.java 完整演示

     編輯:Java 相關知識

四種執行緒狀態回顧

新建、可運行、死亡、**阻塞**。阻塞狀態最有趣,值得深入探討。

五大阻塞原因

  1. sleep(毫秒數): 進入「睡眠」,規定時間內不運行
  2. suspend(): 暫停執行,除非收到 resume() 不會回「可運行」
  3. wait(): 暫停執行,除非收到 notify()/notifyAll() 不會變「可運行」(與原因 2 相似但有明顯區別)
  4. 等待 IO 操作完成: 如讀取網路/檔案
  5. 嘗試調用同步方法但物件被鎖定: 暫時無法使用

yield() 自動放棄 CPU 讓其他執行緒運行。但若調度器覺得我們執行緒已擁有足夠時間並跳轉到另一執行緒,會發生同樣事。沒有什麼能防止調度器重新啟動我們的執行緒。**阻塞後有具體原因導致無法繼續運行**。

Blocking.java:五種阻塞完整演示

所有類別存在於 Blocking.java,此處用片段解釋。

基礎框架

class Blockable extends Thread {
    private Peeker peeker;
    protected TextField state = new TextField(40);
    protected int i;
    public Blockable(Container c) { c.add(state); peeker = new Peeker(this, c); }
    public synchronized int read() { return i; }
    protected synchronized void update() { state.setText(getClass().getName() + " state: i = " + i); }
    public void stopPeeker() { peeker.terminate(); }
}

class Peeker extends Thread {
    private Blockable b;
    private int session;
    private TextField status = new TextField(40);
    private boolean stop = false;
    public Peeker(Blockable b, Container c) { c.add(status); this.b = b; start(); }
    public void terminate() { stop = true; }
    public void run() {
        while (!stop) {
            status.setText(b.getClass().getName() + " Peeker " + (++session) + "; value = " + b.read());
            try { sleep(100); } catch (InterruptedException e){}
        }
    }
}
  • Blockable:基礎類,含 state 顯示資訊,update() 用 getClass().getName() 取得確切類名
  • 變動指示符 int i,衍生類 run() 遞增
  • 每個 Blockable 啟動一個 Peeker 執行緒,定期呼叫 read() 檢查 i 變化並報告
  • read() 和 update() 皆同步,要求物件鎖可自由釋放——**極重要**

1. 睡眠阻塞

class Sleeper1 extends Blockable {
    public synchronized void run() { // 整個 run 同步
        while(true) { i++; update(); try { sleep(1000); } catch (InterruptedException e){} }
    }
}
class Sleeper2 extends Blockable {
    public void run() { // 不同步 run
        while(true) { change(); try { sleep(1000); } catch (InterruptedException e){} }
    }
    public synchronized void change() { i++; update(); }
}
  • Sleeper1: 整個 run() 同步 → 啟動後 Peeker 完全停止(鎖永不釋放)
  • Sleeper2: 只有 change() 同步 → Peeker 持續運行(sleep() 期間鎖已釋放)

2. 暫停/恢復阻塞

class SuspendResume1 extends Blockable {
    public synchronized void run() { while(true) { i++; update(); suspend(); } }
}
class SuspendResume2 extends Blockable {
    public void run() { while(true) { change(); suspend(); } }
    public synchronized void change() { i++; update(); }
}
class Resumer extends Thread { // 獨立恢復器
    private SuspendResume sr;
    public void run() { while(true) { try { sleep(1000); } catch (InterruptedException e){} sr.resume(); } }
}
  • 同樣:同步 run() 導致 Peeker 阻塞;分離同步方法解決
  • Java 1.2 棄用: suspend() 持有物件鎖,極易死結

3. 等待/通知阻塞

class WaitNotify1 extends Blockable {
    public synchronized void run() { while(true) { i++; update(); try { wait(1000); } catch (InterruptedException e){} } }
}
class WaitNotify2 extends Blockable {
    public synchronized void run() { while(true) { i++; update(); try { wait(); } catch (InterruptedException e){} } }
}
class Notifier extends Thread {
    private WaitNotify2 wn2;
    public void run() { while(true) { try { sleep(2000); } catch (InterruptedException e){} synchronized(wn2) { wn2.notify(); } } }
}
  • 關鍵差異: wait() 被調用時**釋放鎖定**!執行期間可調用其他同步方法
  • 兩種形式:wait(毫秒) 定時/可被 notify() 強制結束;wait() 無限期直到 notify()
  • wait()/notify() 屬於 Object 基礎類(操縱物件鎖),非 Thread
  • **只能在同步方法/區塊內呼叫**,否則拋 IllegalMonitorStateException
  • 只能為自己的鎖調用,但可 synchronized(other) { other.notify(); } 為他人鎖操作

4. IO 阻塞

class Sender extends Blockable {
    private Writer out;
    public void run() {
        while(true) {
            for(char c = 'A'; c <= 'z'; c++) {
                try { i++; out.write(c); state.setText("Sender sent: " + c); sleep((int)(3000 * Math.random())); }
                catch (InterruptedException e){} catch (IOException e) {}
            }
        }
    }
}
class Receiver extends Blockable {
    private Reader in;
    public void run() {
        try { while(true) { i++; state.setText("Receiver read: " + (char)in.read()); } }
        catch(IOException e) { e.printStackTrace(); }
    }
}
  • 管道串流 PipedWriter/PipedReader 連接兩執行緒
  • Receiver 無 sleep/suspend/wait,但執行 read() 時無資料 → **自動阻塞**

5. 測試主程式

建立 Blockable 陣列,按下「Start」啟動所有執行緒。「Stop Peekers」示範 Thread.stop() 替代方案。Sender/Receiver 用管道串流連接。初始化時每個 Blockable 自動建立並啟動 Peeker,**部分 Peeker 會在 Blockable 啟動時被阻塞停止運行**——理解此點加深對「阻塞」認識。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved