Thread對象
每個(gè)線程都與Thread類的實(shí)例相關(guān)聯(lián),使用Thread對象創(chuàng)建并發(fā)應(yīng)用程序有兩種基本策略。
要直接控制線程的創(chuàng)建和管理,只需在每次應(yīng)用程序需要啟動(dòng)異步任務(wù)時(shí)實(shí)例化Thread。
要從應(yīng)用程序的其余部分抽象線程管理,請將應(yīng)用程序的任務(wù)傳遞給執(zhí)行器。
本節(jié)介紹Thread對象的使用,Executors將與其他高級并發(fā)對象一起討論。
定義和啟動(dòng)線程創(chuàng)建Thread實(shí)例的應(yīng)用程序必須提供將在該線程中運(yùn)行的代碼,有兩種方法可以做到這一點(diǎn):
提供Runnable對象,Runnable接口定義了一個(gè)多帶帶的run方法,用于包含在線程中執(zhí)行的代碼,Runnable對象被傳遞給Thread構(gòu)造函數(shù),如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(); } }
子類化Thread,Thread類本身實(shí)現(xiàn)了Runnable,盡管它的run方法什么都不做,應(yīng)用程序可以子類化Thread,提供自己的run實(shí)現(xiàn),如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(); } }
請注意,兩個(gè)示例都調(diào)用Thread.start以啟動(dòng)新線程。
你應(yīng)該使用哪個(gè)語法?使用Runnable對象的第一個(gè)語法更通用,因?yàn)?b>Runnable對象可以繼承Thread以外的類。第二個(gè)語法在簡單的應(yīng)用程序中更容易使用,但受限于你的任務(wù)類必須是Thread的后代這一事實(shí)。本課重點(diǎn)介紹第一種方法,該方法將Runnable任務(wù)與執(zhí)行任務(wù)的Thread對象分開,這種方法不僅更靈活,而且適用于后面介紹的高級線程管理API。
Thread類定義了許多對線程管理有用的方法,這些包括靜態(tài)方法,它們提供關(guān)于調(diào)用該方法的線程的信息,或影響該線程的狀態(tài)。其他方法是從管理線程和Thread對象的其他線程調(diào)用的,我們將在以下部分中研究其中一些方法。
用Sleep暫停執(zhí)行Thread.sleep導(dǎo)致當(dāng)前線程暫停執(zhí)行指定的時(shí)間段,這是使處理器時(shí)間可用于應(yīng)用程序的其他線程或可能在計(jì)算機(jī)系統(tǒng)上運(yùn)行的其他應(yīng)用程序的有效方法。sleep方法也可以用于調(diào)步,如下面的示例所示,和等待具有被理解為具有時(shí)間要求的職責(zé)的另一個(gè)線程,如稍后部分中的SimpleThreads示例。
提供了兩個(gè)重載版本的sleep:一個(gè)指定毫秒的睡眠時(shí)間,一個(gè)指定納秒的睡眠時(shí)間。但是,這些睡眠時(shí)間并不能保證精確,因?yàn)樗鼈兪艿降讓硬僮飨到y(tǒng)提供的設(shè)施的限制,此外,睡眠周期可以通過中斷終止,我們將在后面的部分中看到。在任何情況下,你都不能設(shè)想調(diào)用sleep會準(zhǔn)確地在指定的時(shí)間段內(nèi)暫停該線程。
SleepMessages示例使用sleep以四秒為間隔打印消息:
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,這是一個(gè)異常,當(dāng)sleep處于活動(dòng)狀態(tài)時(shí),另一個(gè)線程中斷當(dāng)前線程時(shí),sleep將拋出,由于此應(yīng)用程序尚未定義另一個(gè)導(dǎo)致中斷的線程,因此無需捕獲InterruptedException。
中斷中斷是指示線程應(yīng)該停止正在做的事情,并執(zhí)行其他操作,由程序員決定線程如何響應(yīng)中斷,但用于終止線程是很常見的,這是本課程中強(qiáng)調(diào)的用法。
線程通過調(diào)用Thread對象上的interrupt來發(fā)送中斷,以便線程被中斷,為使中斷機(jī)制正常工作,被中斷的線程必須支持自己的中斷。
支持中斷線程如何支持自己的中斷?這取決于它目前正在做什么,如果線程經(jīng)常調(diào)用拋出InterruptedException的方法,它只會在捕獲該異常后從run方法返回。例如,假設(shè)SleepMessages示例中的中心消息循環(huán)位于線程的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)被設(shè)計(jì)為收到中斷時(shí)取消當(dāng)前操作并立即返回。
如果一個(gè)線程長時(shí)間運(yùn)行而不調(diào)用拋出InterruptedException的方法呢?那么它必須定期調(diào)用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; } }
在這個(gè)簡單的例子中,代碼只是測試中斷,如果收到中斷則退出線程,在更復(fù)雜的應(yīng)用程序中,拋出InterruptedException可能更有意義:
if (Thread.interrupted()) { throw new InterruptedException(); }
這允許中斷處理代碼集中在catch子句中。
中斷狀態(tài)標(biāo)志中斷機(jī)制使用稱為中斷狀態(tài)的內(nèi)部標(biāo)志來實(shí)現(xiàn),調(diào)用Thread.interrupt設(shè)置此標(biāo)志,當(dāng)線程通過調(diào)用靜態(tài)方法Thread.interrupted來檢查中斷時(shí),將清除中斷狀態(tài),非靜態(tài)isInterrupted方法,由一個(gè)線程用于查詢另一個(gè)線程的中斷狀態(tài),不會更改中斷狀態(tài)標(biāo)志。
按照慣例,任何通過拋出InterruptedException退出的方法都會在執(zhí)行此操作時(shí)清除中斷狀態(tài),但是,通過另一個(gè)線程調(diào)用中斷,總是可以立即再次設(shè)置中斷狀態(tài)。
加入join方法允許一個(gè)線程等待另一個(gè)線程的完成,如果t是其線程當(dāng)前正在執(zhí)行的Thread對象:
t.join();
導(dǎo)致當(dāng)前線程暫停執(zhí)行,直到t的線程終止,join重載方法允許程序員指定等待周期,但是,與sleep一樣,join依賴于OS進(jìn)行計(jì)時(shí),因此你不應(yīng)該設(shè)想join將準(zhǔn)確地等待你指定的時(shí)間。
與sleep一樣,join通過InterruptedException退出來響應(yīng)中斷。
SimpleThreads示例以下示例匯總了本節(jié)的一些概念,SimpleThreads由兩個(gè)線程組成。第一個(gè)是每個(gè)Java應(yīng)用程序都有的主線程,主線程從Runnable對象MessageLoop創(chuàng)建一個(gè)新線程,并等待它完成,如果MessageLoop線程需要很長時(shí)間才能完成,主線程會中斷它。
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!"); } }上一篇:進(jìn)程和線程 下一篇:同步
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72993.html
摘要:未來的主要發(fā)布基于。在中調(diào)用函數(shù)支持從代碼中直接調(diào)用定義在腳本文件中的函數(shù)。下面的函數(shù)稍后會在端調(diào)用為了調(diào)用函數(shù),你首先需要將腳本引擎轉(zhuǎn)換為。調(diào)用函數(shù)將結(jié)果輸出到,所以我們會首先看到輸出。幸運(yùn)的是,有一套補(bǔ)救措施。 原文:Java 8 Nashorn Tutorial 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 這個(gè)教程中,你會通過簡單易懂的代碼示例,來了解Nashorn Ja...
摘要:在這個(gè)示例中我們使用了一個(gè)單線程線程池的。在延遲消逝后,任務(wù)將會并發(fā)執(zhí)行。這是并發(fā)系列教程的第一部分。第一部分線程和執(zhí)行器第二部分同步和鎖第三部分原子操作和 Java 8 并發(fā)教程:線程和執(zhí)行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 譯者:BlankKelly 來源:Java8并發(fā)教程:Threads和Execut...
Lock對象 同步代碼依賴于簡單的可重入鎖,這種鎖易于使用,但有許多限制,java.util.concurrent.locks包支持更復(fù)雜的鎖定語法,我們不會詳細(xì)檢查這個(gè)包,而是將重點(diǎn)放在其最基本的接口Lock上。 Lock對象的工作方式與同步代碼使用的隱式鎖定非常相似,與隱式鎖一樣,一次只有一個(gè)線程可以擁有一個(gè)Lock對象,Lock對象還通過其關(guān)聯(lián)的Condition對象支持wait/notif...
摘要:創(chuàng)建一個(gè)阻塞隊(duì)列生產(chǎn)者生產(chǎn),目前總共有消費(fèi)者消費(fèi),目前總共有原文鏈接更多教程 原文鏈接 更多教程 本文概要 生產(chǎn)者和消費(fèi)者問題是線程模型中老生常談的問題,也是面試中經(jīng)常遇到的問題。光在Java中的實(shí)現(xiàn)方式多達(dá)數(shù)十種,更不用說加上其他語言的實(shí)現(xiàn)方式了。那么我們該如何學(xué)習(xí)呢? 本文會通過精講wait()和notify()方法實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型,來學(xué)習(xí)生產(chǎn)者和消費(fèi)者問題的原理。 目的...
閱讀 1608·2023-04-26 01:54
閱讀 1637·2021-09-30 09:55
閱讀 2658·2021-09-22 16:05
閱讀 1873·2021-07-25 21:37
閱讀 2633·2019-08-29 18:45
閱讀 1900·2019-08-29 16:44
閱讀 1895·2019-08-29 12:34
閱讀 1359·2019-08-23 14:02