java並發工具學習 02 線程對象(Thread Object)那些事
每個線程都跟一個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 staticvoid 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)那些事