Java 線程的基本使用


GitHub Page: http://blog.cloudli.top/posts/Java-線程的基本使用/

創建線程

創建線程的方式有兩種:

  • 繼承 Thread
  • 實現 Runnable 接口

Thread 類實現了 Runnable 接口。使用繼承 Thread 類的方式創建線程時,最大的局限是不支持多繼承。所以為了支持多繼承,應該使用實現 Runnable 接口的方式。兩種方式創建的線程在工作時是一樣的,沒有本質區別。

第一種方式,繼承 Thread 類並重寫 run() 方法:

public class Work extends Thread {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Work work = new Work();
        work.start();
        System.out.println("End!");
    }
}

運行結果可能 “End!”先輸出。在使用多線程時,運行結果與調用順序是無關的。

調用 run() 方法只是普通的方法調用,不會啟動線程。如果多次調用 start() 方法,會拋出 IllegalThreadStateException 異常。

第二種方式,實現 Runnable 接口:

public class Work implements Runnable {
    
    @Override
    public void run() {
        System.out.println("Working...");
    }
}

public class Run {
    public static void main(String[] args) {
        Thread t = new Thread(new Work());
        t.start();
        System.out.println("End!");
    }
}

這種方式與第一種在運行上沒有什么區別。其優點在於突破了單繼承的限制。

Thread 類的部分構造方法:

構造方法 說明
Thread() 創建一個新的線程
Thread(String name) 創建一個新的線程,並指定名稱
Thread(Runnable target) 創建一個新的線程,將 target 作為運行對象
Thread(Runnable target, String name) 將 target 作為運行對象,並指定名稱
Thread(ThreadGroup group, Runnable target) 將 target 作為運行對象,並作為線程組的一員

線程的方法

currentThread() 方法

currentThread() 方法返回正在被執行的線程的信息。

public class Run() {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

以上代碼在控制台輸出 “main“,說明該方法被名為 main 的線程調用。

import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("%s 被調用\n", currentThread().getName());
        }
    }

    public static void main(String[] args) {
        Work t1 = new Work(),
                t2 = new Work();
        t1.start();
        t2.start();
    }
}

以上代碼運行結果:

Thread-0 被調用
Thread-1 被調用

Process finished with exit code 0

run() 方法中可以省略 Thread 直接調用 currentThread() 方法。

isAlive() 方法

該方法判斷當前線程是否處於活動狀態。

import static java.lang.System.out;

public class Run {

    static class Work extends Thread {
    
        @Override
        public void run() {
            out.printf("運行中 %s\n", isAlive());
        }
    }

    public static void main(String[] args) throws Throwable {
        Work t = new Work();
        out.printf("運行前: %s\n", t.isAlive());
        t.start();
        // 等待線程運行完成
        Thread.sleep(1000);
        out.printf("運行結束: %s\n", t.isAlive());
    }
}

以上代碼運行結果:

運行前: false
運行中 true
運行結束: false

Process finished with exit code 0

sleep() 方法

sleep() 方法指定毫秒數讓當前線程休眠(暫停運行),該操作不會釋放鎖

停止線程

interrupt() 方法

interrupt() 方法並不能立刻停止線程,只是在在線程中打了一個停止的標記。

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代碼運行結果:

...
i = 8190
i = 8191
i = 8192
Call interrupt!
i = 8193
i = 8194
i = 8195
...

interrupt() 方法調用后,線程仍在運行。

要使用 interrupt() 方法停止線程,需要在線程中判斷中斷狀態,有兩個方法:

  • interrupted():測試當前線程是否是中斷狀態,執行后將狀態清除,設置為 false
  • isInterrupted():作用同上,但是不清除狀態。
import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            for (int i = 1; i <= 50000; i++) {
                if (isInterrupted()) {
                    out.println("跳出循環!");
                    break;
                }
                out.printf("i = %d\n", i);
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代碼執行結果:

...
i = 8301
i = 8302
i = 8303
i = 8304
i = 8305
i = 8306
i = 8307
Call interrupt!
跳出循環!

Process finished with exit code 0

在調用 interrupt() 方法后,循環已經退出。但是這種方式只是跳出了循環,假如 for 循環外還有代碼,仍然會執行。

拋出異常停止線程

可以在判斷線程狀態為中斷時,拋出一個異常,在 catchfinally 塊中做中斷后的處理:

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {

        @Override
        public void run() {
            try {
                for (int i = 1; i <= 50000; i++) {
                    if (isInterrupted()) {
                        out.println("Interrupted!");
                        throw new InterruptedException("拋出異常!");
                    }
                    out.printf("i = %d\n", i);
                }
                out.println("for 循環結束!");
            } catch (InterruptedException e) {
                out.println(e.getMessage());
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(200);
        work.interrupt();
        out.println("Call interrupt!");
    }
}

以上代碼將線程要執行的任務放入 try 塊中,當判斷為中斷狀態時,拋出 InterruptedException ,如果需要釋放鎖,可以在 finally 塊中執行。

也可以配合 return 來停止線程:

if (isInterrupted()) {
    return;
}

暫停線程

Java 提供了 suspend()resume() 方法來暫停和恢復線程,不過這兩個方法已經過期作廢了。

suspend() 方法暫停線程時,不會釋放鎖。所以使用 suspend() 方法容易產生死鎖。

如果需要暫停線程,可以加入一個標記,若標記指出線程需要暫停,使用 wait() 進入等待狀態,如需要恢復,使用 notify() 喚醒。

import static java.lang.System.out;

public class StopThread {

    static class Work extends Thread {
        // 暫停標記
        private boolean isSuspended = false;

        void pause() {
            isSuspended = true;
        }

        synchronized void wake() {
            isSuspended = false;
            // 喚醒
            this.notify();
            out.println("已喚醒!");
        }

        @Override
        public void run() {
            synchronized (this) {
                try {
                    for (int i = 1; i <= 5000; i++) {
                        if (isInterrupted()) {
                            return;
                        }

                        if (isSuspended) {
                            out.println("已暫停!");
                            // 等待
                            this.wait();
                        }

                        out.printf("%s i = %d\n", getName(), i);
                    }
                    out.printf("%s end!\n", getName());
                } catch (InterruptedException e) {
                    out.println(e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) throws Throwable {
        Work work = new Work();
        work.start();
        Thread.sleep(100);
        // 暫停
        work.pause();
        Thread.sleep(100);
        // 喚醒
        work.wake();
    }
}

以上代碼使用 wait()notify() 暫停與恢復線程。運行結果:

...
Thread-0 i = 202
Thread-0 i = 203
Thread-0 i = 204
已暫停!
已喚醒!
Thread-0 i = 205
Thread-0 i = 206
Thread-0 i = 207
...
Thread-0 i = 4998
Thread-0 i = 4999
Thread-0 i = 5000
Thread-0 end!

Process finished with exit code 0

yield 方法

yield() 方法的作用是放棄當前的 CPU 資源,讓其他的任務去占用 CPU 執行時間。但放棄的時間不確定,有可能剛剛放棄,馬上又獲得時間片。

import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;

public class Yield {
    static class Work extends Thread {

        @Override
        public void run() {
            long before = currentTimeMillis();
            int sum = 0;
            for (int i =1; i < 2000000; i++) {
                // yield();
                sum += (i + 1);
            }
            long after = currentTimeMillis();
            out.printf("Cost: %dms\n", after - before);
        }
    }

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

以上代碼不使用 yield() 方法時大概 15ms 執行完,加上后大概有 500ms。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM