摘要:類重點(diǎn)是那個(gè)病毒掃描程序的例子,認(rèn)真看三遍。會從線程池中選擇線程,并將對象提交給線程任務(wù)。比如病毒掃描,你可以使用類實(shí)現(xiàn)的執(zhí)行器服務(wù),每小時(shí)運(yùn)行一次病毒掃描。該應(yīng)用是以固定頻率執(zhí)行的病毒掃描程序。
Executors類
重點(diǎn)是那個(gè)病毒掃描程序的例子,認(rèn)真看三遍。本文花了四個(gè)小時(shí)。
GitHub代碼歡迎star。
小白認(rèn)為學(xué)習(xí)語言最好的方式就是模仿、思考別人為什么這么寫。結(jié)合栗子效果更好,也能記住知識點(diǎn)。
Executors類允許創(chuàng)建線程池并返回ExecutorService對象,執(zhí)行器提供了將任務(wù)提交與對任務(wù)進(jìn)行解耦的標(biāo)準(zhǔn)方法,除了對基本的線程生命周期提供支持外,窒息功能其還提供統(tǒng)計(jì)收集,應(yīng)用管理及監(jiān)控方面的功能。這一切都基于 生產(chǎn)者-消費(fèi)者模式。 使用這種設(shè)計(jì)模式可以對大型并發(fā)應(yīng)用程序很好的進(jìn)行擴(kuò)展。
使用這種服務(wù)對象,可以運(yùn)行Runnable和Callable類的實(shí)力,你只需要做的是提交任務(wù)給服務(wù)對象就可以。ExecutorService會從線程池中選擇線程,并將Runnable對象提交給線程任務(wù)。當(dāng)任務(wù)結(jié)束時(shí),線程并不會銷毀 ,而是返回到線程池中繼續(xù)執(zhí)行后續(xù)的其他任務(wù),這樣可以 避免創(chuàng)建和銷毀線程帶來的額外開銷
Executors類有許多靜態(tài)方法可用來創(chuàng)建線程池:1、newFixedTHreadPool方法能夠創(chuàng)建固定大小的線程池。線程池中的線程將被用來處理任務(wù)請求,如果線程處于空閑狀態(tài),線程不會銷毀,而是會被存放線程池中一段不確定的是將
2、newCachedThreadPool,使用該方法創(chuàng)建的線程池中的線程會在空閑60秒之后自動銷毀,
3、newSingleThreadExecutor該方法僅創(chuàng)建一個(gè)線程,當(dāng)任務(wù)結(jié)束后不會銷毀而是用于處理其他任務(wù)。對于多個(gè)任務(wù)同時(shí)請求,則使用隊(duì)列來維護(hù)所有待處理的請求。隨后會順序執(zhí)行。
4、newScheduledThreadPool可以把它看作是java.util.Timer的替代品,該方法創(chuàng)建固定大小的線程池用來調(diào)度執(zhí)行任務(wù),并返回一個(gè)ScheduledExecutorService對象,該對象提供了若干個(gè)方法用于執(zhí)行任務(wù)的調(diào)度執(zhí)行。
創(chuàng)建線程池以進(jìn)行任務(wù)調(diào)度有時(shí)創(chuàng)建可在一定時(shí)間延遲后執(zhí)行的線程,可以設(shè)置一個(gè)報(bào)警器在一段時(shí)間過后報(bào)警。在某些情況下,你也希望以 一定的頻率或固定時(shí)間間隔反復(fù)執(zhí)行線程。
比如病毒掃描,你可以使用newScheduledThreadPool類實(shí)現(xiàn)的執(zhí)行器服務(wù),每24小時(shí)運(yùn)行一次病毒掃描。如果有多個(gè)磁盤或大容量的磁盤需要掃描,將掃描的任務(wù)分解為多個(gè)單元。讓每個(gè)單元掃描某個(gè)特定的磁盤。
另一凸顯此服務(wù)很實(shí)用的應(yīng)用場景是新聞聚合器。聚合器從多個(gè)新聞源收集最新新聞,并將它們排列在客戶端以供閱讀,多個(gè)數(shù)據(jù)源獲取可以并發(fā)執(zhí)行,而這根據(jù)目標(biāo)數(shù)據(jù)源的網(wǎng)絡(luò)狀況,花費(fèi)的時(shí)間會不一樣。客戶端和數(shù)據(jù)源的同步會周期性地執(zhí)行。如果這樣的同步操作頻率很高,新的同步操作和當(dāng)前正在執(zhí)行的操作就有可能出現(xiàn)重疊。在這種情況下,最好給每次任務(wù)的執(zhí)行設(shè)固定的時(shí)間間隔,ScheduledExecutosService可以幫你實(shí)現(xiàn)這樣的需求。
ScheduledExecutorService類1、ScheduledExecutorService 類提供了名為schedule的方法用于設(shè)定任務(wù)的未來執(zhí)行。schedule方法有兩個(gè)重載版本:
//Creates and executes a ScheduledFuture that becomes enabled after the given delay.ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) //Creates and executes a one-shot action that becomes enabled after the given delay. ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit)
schedule方法接收三個(gè)參數(shù):Callable和Runnable接口、延遲時(shí)間以及時(shí)間單位。該方法安排由 Callable和Runnable指定的任務(wù)在給定的延遲時(shí)間后執(zhí)行。時(shí)間單位 由該方法的第三個(gè)參數(shù)指定。方法會返回一個(gè)Future對象給調(diào)用方。
2、除了這個(gè)簡單的延遲執(zhí)行之外,ScheduledExecutorService類還提供了scheduleAtFixedRate方法,該任務(wù)可以指定任務(wù)按照一定的頻率執(zhí)行。
//Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; //that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on. ScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
第一次執(zhí)行發(fā)生在給定的延遲之后,后續(xù)執(zhí)行發(fā)生在“延遲+固定時(shí)間”,“延遲+2*固定周期”,依次類推,這種方法可以用于病毒掃描
3、scheduleWithFiedDelay方法在給定延遲之后第一次執(zhí)行任務(wù)。之后按照固定好的時(shí)間間隔執(zhí)行,時(shí)間間隔遞歸你以為本次任務(wù)運(yùn)行到下一次任務(wù)的開始。這類調(diào)度可以用于新聞聚合應(yīng)用。
//Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay between the termination of one execution and the commencement of the next. ScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)任務(wù)的調(diào)度執(zhí)行(重點(diǎn)在匿名線程)
/** * Created by guo on 2018/2/16. * 演示任務(wù)調(diào)度執(zhí)行 * 需求: * 如何讓任務(wù)以一定的頻率執(zhí)行。 * 1、該應(yīng)用是以固定頻率執(zhí)行的病毒掃描程序。 * 2、當(dāng)掃描開始時(shí),程序彈出窗口以顯示掃描進(jìn)度,當(dāng)磁盤上所有文件被掃描之后,任務(wù)會停止。 * 3、每次掃描都需要不同的時(shí)間,通過讓線程隨機(jī)睡眠一段時(shí)間來模擬這個(gè)過程。 * 4、掃描結(jié)束之后,狀態(tài)窗口會被關(guān)閉,知道下次掃描才會彈出, */ public class VirusScanner { private static JFrame appFrame; private static JLabel statusString; private int scanNumber = 0; //1、調(diào)用Executors類的newScheduledThreadPool方法來創(chuàng)建線程池。 private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5); private static final GregorianCalendar calendar = new GregorianCalendar(); private static VirusScanner app = new VirusScanner(); /** * scanDisk方法執(zhí)行實(shí)際的掃描工作 */ public void scanDisk() { //2、使用線程池中的線程來解決多重并發(fā)掃描。 final Runnable scanner = new Runnable() { @Override public void run() { try { //將狀態(tài)窗口顯示給用戶 appFrame.setVisible(true); scanNumber++; Calendar cal = Calendar.getInstance(); //顯示掃描數(shù)以及掃描開始時(shí)間,接下來,讓當(dāng)前線程隨機(jī)睡一段時(shí)間。 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM); statusString.setText(" Scan" + scanNumber + " started at" + df.format(cal.getTime())); //常數(shù)1000是用來確保窗口至少顯示1秒。在實(shí)際程序中,病毒掃描代碼會放在sleep語句所在的位置。 //讓線程休眠是假裝病毒掃描持續(xù)一段時(shí)間, //當(dāng)線程從休眠中喚醒時(shí),我們隱藏了窗口,這讓用戶感覺當(dāng)前一輪已經(jīng)結(jié)束。 //題外話1:請卸載國產(chǎn)360,QQ管家,小白可以無視。需要的組件可以下載綠色版。(明明是一個(gè)開源軟件,你卻說那高危險(xiǎn)。明明是https://www.github.com開頭。) //題外話2:感謝 架構(gòu)@奇虎360,@江湖人稱小白哥。謝謝你的心意,能力沒到那,你還不能成為我職業(yè)生涯的第一位貴人。騷年,加油吧,越努力,越幸運(yùn)。 Thread.sleep(1000 + new Random().nextInt(10000)); } catch (InterruptedException e) { e.printStackTrace(); } } }; //重點(diǎn):3、使用之前創(chuàng)建的調(diào)度器來讓掃描程序以固定頻率執(zhí)行。 // a、掃描任務(wù)在最初的一秒延遲之后會以每隔15秒的頻率運(yùn)行 // b、調(diào)用器會返回一個(gè)Future對象,用于之后取消掃描任務(wù)。 // c、為了能夠進(jìn)行取消操作,創(chuàng)建另一個(gè)匿名線程。 // d、以下代碼所有時(shí)間單位為秒,目前只是模擬的效果。 // e、在實(shí)際應(yīng)用中,病毒掃描應(yīng)當(dāng)每天或每幾小時(shí)執(zhí)行一次 final ScheduledFuture> scanManager = scheduler.scheduleAtFixedRate(scanner, 1, 15, TimeUnit.SECONDS); /** * 匿名線程 * 這個(gè)線程只在60秒延遲之后運(yùn)行一次,模擬會以一分鐘的總時(shí)間周期執(zhí)行 * 每隔15秒,病毒掃描狀態(tài)窗口會彈出,并且顯示請留1秒,或更長時(shí)間。 */ scheduler.schedule(new Runnable() { @Override public void run() { //4、取消病毒掃描任務(wù),并關(guān)閉調(diào)度器和狀態(tài)窗口 scanManager.cancel(true); scheduler.shutdown(); appFrame.dispose(); } }, 60, TimeUnit.SECONDS); } }主函數(shù)(不是重點(diǎn))
/** * 不是重點(diǎn)的main方法: * 創(chuàng)建狀態(tài)窗口、設(shè)置并調(diào)用scanDisk方法。 * 注意:主線程會在之后立刻結(jié)束,而在scanDisk方法中創(chuàng)建的線程會在接下來一分鐘內(nèi)繼續(xù)運(yùn)行。 */ public static void main(String[] args) { appFrame = new JFrame(); Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize(); appFrame.setSize(400, 70); appFrame.setLocation(dimension.width / 2 - appFrame.getWidth() / 2, dimension.height / 2 - appFrame.getHeight() / 2); statusString = new JLabel(); appFrame.add(statusString); appFrame.setVisible(false); app.scanDisk(); }獲取首個(gè)已結(jié)束的運(yùn)行結(jié)果
之前已經(jīng)學(xué)了如何將任務(wù)提交給執(zhí)行器立即執(zhí)行、延遲以及周期性的運(yùn)行 (計(jì)算年銷售額) 還了解到執(zhí)行器可以提供并維護(hù)多個(gè)線程并發(fā)的執(zhí)行任務(wù) (模擬可取消任務(wù)的股票交易處理程序) 。在某些情況下,當(dāng)提交多個(gè)任務(wù)給執(zhí)行器,你可能希望處理任意以結(jié)束任務(wù)的結(jié)果,而不像等到每個(gè)任務(wù)都執(zhí)行結(jié)束。目前只用過執(zhí)行器的get方法會等待任務(wù)結(jié)束。當(dāng)任務(wù)提交時(shí),可以創(chuàng)建循環(huán)來獲取每個(gè)計(jì)算結(jié)果,代碼如下:
for(Futureresult : results) { result.get(); }
這樣就可以順序的獲取結(jié)果,但如果某個(gè)特定的任務(wù)需要長時(shí)間才能結(jié)束,那么當(dāng)前的get調(diào)用會一直阻塞.在這種情況下,即使其他任務(wù)已經(jīng)提前完成,也無法獲取結(jié)果,為了解決這個(gè)問題,可以使用ExecutorCompletionService類,該類會檢測提交給執(zhí)行器的任務(wù),通過take方法,可以一個(gè)個(gè)地獲取到任務(wù)執(zhí)行的結(jié)果。
待續(xù)...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/68491.html
摘要:有三種狀態(tài)運(yùn)行關(guān)閉終止。類類,提供了一系列工廠方法用于創(chuàng)建線程池,返回的線程池都實(shí)現(xiàn)了接口。線程池的大小一旦達(dá)到最大值就會保持不變,在提交新任務(wù),任務(wù)將會進(jìn)入等待隊(duì)列中等待。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 這是java高并發(fā)系列第19篇文章。 本文主要內(nèi)容 介紹Executor框架相關(guān)內(nèi)容 介紹Executor 介紹ExecutorService 介紹線程池ThreadP...
摘要:在這個(gè)示例中我們使用了一個(gè)單線程線程池的。在延遲消逝后,任務(wù)將會并發(fā)執(zhí)行。這是并發(fā)系列教程的第一部分。第一部分線程和執(zhí)行器第二部分同步和鎖第三部分原子操作和 Java 8 并發(fā)教程:線程和執(zhí)行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 譯者:BlankKelly 來源:Java8并發(fā)教程:Threads和Execut...
摘要:線程的啟動與銷毀都與本地線程同步。操作系統(tǒng)會調(diào)度所有線程并將它們分配給可用的。框架的成員主要成員線程池接口接口接口以及工具類。創(chuàng)建單個(gè)線程的接口與其實(shí)現(xiàn)類用于表示異步計(jì)算的結(jié)果。參考書籍并發(fā)編程的藝術(shù)方騰飛魏鵬程曉明著 在java中,直接使用線程來異步的執(zhí)行任務(wù),線程的每次創(chuàng)建與銷毀需要一定的計(jì)算機(jī)資源開銷。每個(gè)任務(wù)創(chuàng)建一個(gè)線程的話,當(dāng)任務(wù)數(shù)量多的時(shí)候,則對應(yīng)的創(chuàng)建銷毀開銷會消耗大量...
摘要:是一個(gè)中的工具類提供工廠方法來創(chuàng)建不同類型的線程池從上圖中也可以看出的創(chuàng)建線程池的方法創(chuàng)建出來的線程池都實(shí)現(xiàn)了接口常用方法有以下幾個(gè)創(chuàng)建固定數(shù)目線程的線程池超出的線程會在隊(duì)列中等待創(chuàng)建一個(gè)可緩存線程池如果線程池長度超過處理需要可靈活回收空閑 Executors Executors 是一個(gè)Java中的工具類. 提供工廠方法來創(chuàng)建不同類型的線程池. showImg(https://segm...
摘要:能夠異步的執(zhí)行任務(wù),并且通常管理一個(gè)線程池。這樣我們就不用手動的去創(chuàng)建線程了,線程池中的所有線程都將被重用。在之后不能再提交任務(wù)到線程池。它不使用固定大小的線程池,默認(rèn)情況下是主機(jī)的可用內(nèi)核數(shù)。 原文地址: Java 8 Concurrency Tutorial: Threads and Executors Java 5 初次引入了Concurrency API,并在隨后的發(fā)布版本中...
閱讀 2473·2021-11-24 09:39
閱讀 3406·2021-11-15 11:37
閱讀 2251·2021-10-08 10:04
閱讀 3965·2021-09-09 11:54
閱讀 1883·2021-08-18 10:24
閱讀 1034·2019-08-30 11:02
閱讀 1793·2019-08-29 18:45
閱讀 1651·2019-08-29 16:33