Java™ 教程(Thread對象)

Thread對象

每一個線程都與Thread類的實例相關聯,使用Thread對象建立併發應用程序有兩種基本策略。html

  • 要直接控制線程的建立和管理,只需在每次應用程序須要啓動異步任務時實例化Thread
  • 要從應用程序的其他部分抽象線程管理,請將應用程序的任務傳遞給執行器。

本節介紹Thread對象的使用,Executors將與其餘高級併發對象一塊兒討論。java

定義和啓動線程

建立Thread實例的應用程序必須提供將在該線程中運行的代碼,有兩種方法能夠作到這一點:git

  • 提供Runnable對象,Runnable接口定義了一個單獨的run方法,用於包含在線程中執行的代碼,Runnable對象被傳遞給Thread構造函數,如HelloRunnable示例中所示:
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();
    }

}
  • 子類化ThreadThread類自己實現了Runnable,儘管它的run方法什麼都不作,應用程序能夠子類化Thread,提供本身的run實現,如HelloThread示例中所示:
public class HelloThread extends Thread {

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

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

}

請注意,兩個示例都調用Thread.start以啓動新線程。程序員

你應該使用哪一個語法?使用Runnable對象的第一個語法更通用,由於Runnable對象能夠繼承Thread之外的類。第二個語法在簡單的應用程序中更容易使用,但受限於你的任務類必須是Thread的後代這一事實。本課重點介紹第一種方法,該方法將Runnable任務與執行任務的Thread對象分開,這種方法不只更靈活,並且適用於後面介紹的高級線程管理API。github

Thread類定義了許多對線程管理有用的方法,這些包括靜態方法,它們提供關於調用該方法的線程的信息,或影響該線程的狀態。其餘方法是從管理線程和Thread對象的其餘線程調用的,咱們將在如下部分中研究其中一些方法。segmentfault

用Sleep暫停執行

Thread.sleep致使當前線程暫停執行指定的時間段,這是使處理器時間可用於應用程序的其餘線程或可能在計算機系統上運行的其餘應用程序的有效方法。sleep方法也能夠用於調步,以下面的示例所示,和等待具備被理解爲具備時間要求的職責的另外一個線程,如稍後部分中的SimpleThreads示例。api

提供了兩個重載版本的sleep:一個指定毫秒的睡眠時間,一個指定納秒的睡眠時間。可是,這些睡眠時間並不能保證精確,由於它們受到底層操做系統提供的設施的限制,此外,睡眠週期能夠經過中斷終止,咱們將在後面的部分中看到。在任何狀況下,你都不能設想調用sleep會準確地在指定的時間段內暫停該線程。併發

SleepMessages示例使用sleep以四秒爲間隔打印消息:oracle

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處於活動狀態時,另外一個線程中斷當前線程時,sleep將拋出,因爲此應用程序還沒有定義另外一個致使中斷的線程,所以無需捕獲InterruptedException異步

中斷

中斷是指示線程應該中止正在作的事情,並執行其餘操做,由程序員決定線程如何響應中斷,但用於終止線程是很常見的,這是本課程中強調的用法。

線程經過調用Thread對象上的interrupt來發送中斷,以便線程被中斷,爲使中斷機制正常工做,被中斷的線程必須支持本身的中斷。

支持中斷

線程如何支持本身的中斷?這取決於它目前正在作什麼,若是線程常常調用拋出InterruptedException的方法,它只會在捕獲該異常後從run方法返回。例如,假設SleepMessages示例中的中心消息循環位於線程的Runnable對象的run方法中,而後能夠按以下方式修改它以支持中斷:

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的方法呢?那麼它必須按期調用Thread.interrupted,若是收到中斷,則返回true,例如:

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

在這個簡單的例子中,代碼只是測試中斷,若是收到中斷則退出線程,在更復雜的應用程序中,拋出InterruptedException可能更有意義:

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

這容許中斷處理代碼集中在catch子句中。

中斷狀態標誌

中斷機制使用稱爲中斷狀態的內部標誌來實現,調用Thread.interrupt設置此標誌,當線程經過調用靜態方法Thread.interrupted來檢查中斷時,將清除中斷狀態,非靜態isInterrupted方法,由一個線程用於查詢另外一個線程的中斷狀態,不會更改中斷狀態標誌。

按照慣例,任何經過拋出InterruptedException退出的方法都會在執行此操做時清除中斷狀態,可是,經過另外一個線程調用中斷,老是能夠當即再次設置中斷狀態。

加入

join方法容許一個線程等待另外一個線程的完成,若是t是其線程當前正在執行的Thread對象:

t.join();

致使當前線程暫停執行,直到t的線程終止,join重載方法容許程序員指定等待週期,可是,與sleep同樣,join依賴於OS進行計時,所以你不該該設想join將準確地等待你指定的時間。

sleep同樣,join經過InterruptedException退出來響應中斷。

SimpleThreads示例

如下示例彙總了本節的一些概念,SimpleThreads由兩個線程組成。第一個是每一個Java應用程序都有的主線程,主線程從Runnable對象MessageLoop建立一個新線程,並等待它完成,若是MessageLoop線程須要很長時間才能完成,主線程會中斷它。

MessageLoop線程打印出一系列消息,若是在打印完全部消息以前被中斷,MessageLoop線程將打印一條消息並退出。

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!");
    }
}

上一篇:進程和線程

下一篇:同步

相關文章
相關標籤/搜索