1. 程式人生 > >java並發工具學習 02 線程對象(Thread Object)那些事

java並發工具學習 02 線程對象(Thread Object)那些事

納秒 imp ber 利用 delay 如果 epm 基礎 接口

每個線程都跟一個Thread實例關聯。有兩種建立線程的基礎策略。
+實例化一個Thread實例,程序異步新建一個線程執行任務(方便直接控制線程的創建和管理)
+傳送任務給執行器(executor)執行(從應用的其他部分抽象化線程的管理)
本節使用方法一建立線程,執行器後面章節會介紹到。

1、定義並建立一個線程
定義一個線程需定義線程需提供線程執行的業務邏輯,有兩種方式實現:
(1)實現Runable接口

public class HelloRunnable implements Runnable {

    public void run() {
        System.out.println(
"Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }

(2)繼承Thread類,實現方法run()

public class HelloThread extends Thread {

    public void run() {
        System.out.println("Hello from a thread!");
    }

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

上述的兩種風格你應該怎麽選擇呢。第一種風格是比較常見的,因為實現runnable接口後還可以繼承Thread類之外的類。第二種風格在一個簡單應用中很容易使用,但受限於它是Thread的子孫類。本節主要使用風格一,它可以將線程(Thread)與任務(Runnable)分離。

風格一不僅比風格而靈活,而且比風格而更適用於高級API(線程池等)。

2、利用睡眠(Sleep)暫停一個線程
Thread.sleep可以暫停當前線程並指定暫停的時間。暫停時,線程的時間片將會讓給系統中其他運行的線程或者進程使用。
sleep方法重載了兩個版本,一個提供精確到毫秒,另一個則能精確到納秒。
當然,睡眠時間不能保證精確的,這受限於系統設備。同時線程也可以在睡眠期間因線程中斷(interrupts)而終止。

public class SleepMessages {
    public static void main(String args[])
        throws InterruptedException {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };

        for (int i = 0;
             i < importantInfo.length;
             i++) {
            //Pause for 4 seconds
            Thread.sleep(4000);
            //Print a message
            System.out.println(importantInfo[i]);
        }
    }
}

可以看到main函數拋出InterruptedException異常。這個異常是其他線程在當前線程sleep時候對當前進程進行中斷操作觸發的。

3、線程中斷(interrupts)
中斷(interrupt)預示著線程應當停止正在做的事。由開發者來決定線程如何響應中斷。
通過調用線程的interrupt方法來對線程發送中斷命令。為了讓中斷機制能正確地工作,被中斷線程應該能支持中斷。
(1)支持中斷
如何做能讓一個線程支持中斷呢?這取決於這個線程正在整啥犢子。如果它調用一個拋出InterruptedException,通常做法是獲取(catch)異常後直接返回(return)。如上面的例子中,如果在runnable中執行,那應修改成下面:

for (int i = 0; i < importantInfo.length; i++) {
    // Pause for 4 seconds
    try {
        Thread.sleep(4000);
    } catch (InterruptedException e) {
        // We‘ve been interrupted: no more messages.
        return;
    }
    // Print a message
    System.out.println(importantInfo[i]);
}

很多方法是拋出InterruptedException的,如sleep,我們可以如上處理。

但是如果一個線程執行很久且調用的方法都不拋InterruptedException的又應該怎麽處理呢?答案是周期性地調用InterruptedException,如果線程被中斷則返回true。如下:

for (int i = 0; i < inputs.length; i++) {
    heavyCrunch(inputs[i]);
    if (Thread.interrupted()) {
        // We‘ve been interrupted: no more crunching.
        return;
    }
}

上面檢測到中斷直接退出,而通常在更復雜的程序中會手動拋出一個異常:

if (Thread.interrupted()) {
    throw new InterruptedException();
}

(2)中斷標誌(The Interrupt Status Flag)

中斷機制(interrupt mechanism)通過線程內部的一個中斷標誌來判斷線程中斷狀態。調用interrupt方法時候給這個標誌設值。當線程通過調用Thread.interrupted來查看線程狀態時候 該標誌又會被清空。用非靜態方法isInterrupted 查詢,既不會清空標誌,又能查到狀態。
當拋出InterruptedException異常時候,也會清空標誌。

4、加入(joins)
join函數可讓一個線程等待另一個線程運行結束再繼續運行。假如t是一個正在跑的線程,
調用

t.join()

將會使當前線程暫停知道t線程終止。join函數的重載函數能指定等待的時間長,當然,這個時間是不能保證精確的。
join、sleep 都會因為線程中斷拋出異常。

5、程序示例

public class SimpleThreads {

    // Display a message, preceded by
    // the name of the current thread
    static void threadMessage(String message) {
        String threadName =
            Thread.currentThread().getName();
        System.out.format("%s: %s%n",
                          threadName,
                          message);
    }

    private static class MessageLoop
        implements Runnable {
        public void run() {
            String importantInfo[] = {
                "Mares eat oats",
                "Does eat oats",
                "Little lambs eat ivy",
                "A kid will eat ivy too"
            };
            try {
                for (int i = 0;
                     i < importantInfo.length;
                     i++) {
                    // Pause for 4 seconds
                    Thread.sleep(4000);
                    // Print a message
                    threadMessage(importantInfo[i]);
                }
            } catch (InterruptedException e) {
                threadMessage("I wasn‘t done!");
            }
        }
    }

    public static void main(String args[])
        throws InterruptedException {

        // Delay, in milliseconds before
        // we interrupt MessageLoop
        // thread (default one hour).
        long patience = 1000 * 60 * 60;

        // If command line argument
        // present, gives patience
        // in seconds.
        if (args.length > 0) {
            try {
                patience = Long.parseLong(args[0]) * 1000;
            } catch (NumberFormatException e) {
                System.err.println("Argument must be an integer.");
                System.exit(1);
            }
        }

        threadMessage("Starting MessageLoop thread");
        long startTime = System.currentTimeMillis();
        Thread t = new Thread(new MessageLoop());
        t.start();

        threadMessage("Waiting for MessageLoop thread to finish");
        // loop until MessageLoop
        // thread exits
        while (t.isAlive()) {
            threadMessage("Still waiting...");
            // Wait maximum of 1 second
            // for MessageLoop thread
            // to finish.
            t.join(1000);
            if (((System.currentTimeMillis() - startTime) > patience)
                  && t.isAlive()) {
                threadMessage("Tired of waiting!");
                t.interrupt();
                // Shouldn‘t be long now
                // -- wait indefinitely
                t.join();
            }
        }
        threadMessage("Finally!");
    }
}

java並發工具學習 02 線程對象(Thread Object)那些事