Java多執行緒入門

N1ce2cu發表於2024-07-18

建立執行緒的三種方式


繼承Thread類

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + " " + i);
        }
    }

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

實現Runnable介面

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable, "T1").start();
        new Thread(myRunnable, "T2").start();
        new Thread(myRunnable, "T3").start();
    }
}

實現Callable介面

class CallerTask implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "回撥結果";
    }

    public static void main(String[] args) {
        // 建立非同步任務
        FutureTask<String> task = new FutureTask<>(new CallerTask());
        // 啟動執行緒
        new Thread(task).start();
        try {
            // 等待執行完成,並獲取返回結果
            String result = task.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • run():封裝執行緒執行的程式碼,直接呼叫相當於呼叫普通方法。

  • start():啟動執行緒,然後由 JVM 呼叫此執行緒的 run() 方法。

  • 實現 Runnable 介面避免了 Java 單繼承的侷限性,Java 不支援多重繼承,因此如果我們的類已經繼承了另一個類,就不能再繼承 Thread 類了。並且適合多個相同的程式程式碼去處理同一資源的情況,把執行緒、程式碼和資料有效的分離,更符合物件導向的設計思想。Callable 介面與 Runnable 非常相似,但可以返回一個結果。

控制執行緒的其他方法

  • sleep():使當前正在執行的執行緒暫停指定的毫秒數,也就是進入休眠的狀態。

  • join():等待這個執行緒執行完才會輪到後續執行緒得到 cpu 的執行權。

public static void main(String[] args) {
    MyRunnable myRunnable = new MyRunnable();
    Thread t1 = new Thread(myRunnable, "T1");
    t1.start();

    try {
        // t1執行完才會輪到後面的執行緒執行
        t1.join();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }

    new Thread(myRunnable, "T2").start();
    new Thread(myRunnable, "T3").start();
}
  • setDaemon():將此執行緒標記為守護執行緒,準確來說,就是服務其他的執行緒,像 Java 中的垃圾回收執行緒,就是典型的守護執行緒。

  • yield():yield() 方法是一個靜態方法,用於暗示當前執行緒願意放棄其當前的時間片,允許其他執行緒執行。然而,它只是向執行緒排程器提出建議,排程器可能會忽略這個建議。具體行為取決於作業系統和 JVM 的執行緒排程策略。

class YieldExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(YieldExample::printNumbers, "劉備");
        Thread thread2 = new Thread(YieldExample::printNumbers, "關羽");

        thread1.start();
        thread2.start();
        /*
            關羽 讓出控制權...
            劉備 讓出控制權...
            關羽: 3
            關羽: 4
            關羽 讓出控制權...             // 即便有時候讓出了控制權,其他執行緒也不一定會執行
            關羽: 5
            劉備: 3
            劉備: 4
            劉備 讓出控制權...
            劉備: 5
         */
    }

    private static void printNumbers() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);

            // 當 i 是偶數時,當前執行緒暫停執行
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + " 讓出控制權...");
                Thread.yield();
            }
        }
    }
}

執行緒的生命週期

相關文章