Java 併發程式設計學習筆記 05 :如何暫停執行緒?

明月燃雨發表於2020-10-01

一、基礎用法

和終止執行緒不同,暫停執行緒意味著此執行緒還可以恢復,在 Java 中,可以使用 suspend() 方法暫停執行緒,然後使用 resume() 方法恢復執行緒的執行。

首先通過一個例子來了解一下這兩個方法的基礎用法,程式碼如下:

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        
        System.out.println("執行緒開始執行");
        myThread.start();
        Thread.sleep(100);
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
        
        System.out.println("執行緒暫停執行");
        myThread.suspend();
        Thread.sleep(100);
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
        
        System.out.println("執行緒恢復執行");
        myThread.resume();
        Thread.sleep(100);
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
        System.out.println(myThread.getI());
    }
}

class MyThread extends Thread {
    private long i = 0;

    public long getI() {
        return i;
    }

    public void setI(long i) {
        this.i = i;
    }

    @Override
    public void run() {
        while (true) {
            i++;
        }
    }
}

控制檯輸出如下:

執行緒開始執行
168569210
168612106
168619437
執行緒暫停執行
168657087
168657087
168657087
執行緒恢復執行
336960390
337119218
337155923

程式碼主要分為三部分,分別是開始、暫停和恢復,對應控制檯的輸出我們可以很清楚地看到:在開始和暫停之間的三次輸出是不同的,說明執行緒正在執行;在暫停和恢復之間的三次輸出是一樣的,說明執行緒已暫停;在恢復之後的三次輸出是不同的,說明執行緒已恢復執行。

二、缺點

使用這兩種方法進行執行緒的暫停與恢復很容易出現獨佔鎖資料不完整的情況。下面我會對這兩種情況分別舉例說明。

2.1 獨佔鎖

public class Run {
    public static void main(String[] args) throws InterruptedException {
        SynchronizedObject synchronizedObject = new SynchronizedObject();
        Thread t1 = new Thread() {
            @Override
            public void run() {
                synchronizedObject.printString();
            }
        };
        t1.setName("a");
        t1.start();

        Thread.sleep(1000);

        Thread t2 = new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "確實開啟了執行緒");
                synchronizedObject.printString();
                System.out.println("但我是不會輸出的");
            }
        };
        t2.setName("b");
        t2.start();
    }
}

class SynchronizedObject {
    synchronized public void printString() {
        System.out.println("-----start-----");
        if ("a".equals(Thread.currentThread().getName())) {
            System.out.println("執行緒 a 永遠暫停了!");
            Thread.currentThread().suspend();
        }
        System.out.println("------end------");
    }
}

控制檯輸出:

-----start-----
執行緒 a 永遠暫停了!
b確實開啟了執行緒

執行緒 a 先執行 printString() 方法,在輸出控制檯的前兩行後又執行了 suspend() 方法永久暫停了執行緒,並且沒有釋放物件鎖;然後執行緒 b 開始執行,等到執行 printString() 方法時因為無法獲取物件鎖而無法繼續執行。

2.2 資料不完整

public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyObject myObject = new MyObject();
        Thread thread = new Thread() {
            @Override
            public void run() {
                myObject.setValue("zmy", "123");
            }
        };
        thread.setName("a");
        thread.start();
        Thread.sleep(2000);
        myObject.printString();
    }
}

class MyObject {
    private String username = "root";
    private String password = "root";

    public void setValue(String username, String password) {
        this.username = username;
        if ("a".equals(Thread.currentThread().getName())) {
            System.out.println("暫停執行緒 a!");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }

    public void printString() {
        System.out.println("username = " + this.username);
        System.out.println("password = " + this.password);
    }
}

控制檯輸出如下:

暫停執行緒 a!
username = zmy
password = root

相關文章