主线程如何等待多线程完成 返回数据_多线程基础体系知识清单
作者:Object
來源:https://juejin.im/user/5d53e1f6f265da03af19cae0/posts
前言
本文會介紹Java中多線程與并發(fā)的基礎(chǔ),適合初學(xué)者食用。
線程與進(jìn)程的區(qū)別
在計(jì)算機(jī)發(fā)展初期,每臺計(jì)算機(jī)是串行地執(zhí)行任務(wù)的,如果碰上需要IO的地方,還需要等待長時(shí)間的用戶IO,后來經(jīng)過一段時(shí)間有了批處理計(jì)算機(jī),其可以批量串行地處理用戶指令,但本質(zhì)還是串行,還是不能并發(fā)執(zhí)行。
如何解決并發(fā)執(zhí)行的問題呢?于是引入了進(jìn)程的概念,每個(gè)進(jìn)程獨(dú)占一份內(nèi)存空間,進(jìn)程是內(nèi)存分配的最小單位,相互間運(yùn)行互不干擾且可以相互切換,現(xiàn)在我們所看到的多個(gè)進(jìn)程“同時(shí)"在運(yùn)行,實(shí)際上是進(jìn)程高速切換的效果。
那么有了線程之后,我們的計(jì)算機(jī)系統(tǒng)看似已經(jīng)很完美了,為什么還要進(jìn)入線程呢?如果一個(gè)進(jìn)程有多個(gè)子任務(wù),往往一個(gè)進(jìn)程需要逐個(gè)去執(zhí)行這些子任務(wù),但往往這些子任務(wù)是不相互依賴的,可以并發(fā)執(zhí)行,所以需要CPU進(jìn)行更細(xì)粒度的切換。所以就引入了線程的概念,線程隸屬于某一個(gè)進(jìn)程,它共享進(jìn)程的內(nèi)存資源,相互間切換更快速。
進(jìn)程與線程的區(qū)別:
Java中進(jìn)程與線程的關(guān)系:
線程的start方法和run方法的區(qū)別
區(qū)別
Java中創(chuàng)建線程的方式有兩種,不管使用繼承Thread的方式還是實(shí)現(xiàn)Runnable接口的方式,都需要重寫run方法。調(diào)用start方法會創(chuàng)建一個(gè)新的線程并啟動,run方法只是啟動線程后的回調(diào)函數(shù),如果調(diào)用run方法,那么執(zhí)行run方法的線程不會是新創(chuàng)建的線程,而如果使用start方法,那么執(zhí)行run方法的線程就是我們剛剛啟動的那個(gè)線程。
程序驗(yàn)證
public?class?Main?{????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread(new?SubThread());????????thread.run();????????thread.start();????}}class?SubThread?implements?Runnable{????@Override????public?void?run()?{????????//?TODO?Auto-generated?method?stub????????System.out.println("執(zhí)行本方法的線程:"+Thread.currentThread().getName());????}}Thread和Runnable的關(guān)系
Thread源碼
Runnable源碼
區(qū)別
通過上述源碼圖,不難看出,Thread是一個(gè)類,而Runnable是一個(gè)接口,Runnable接口中只有一個(gè)沒有實(shí)現(xiàn)的run方法,可以得知,Runnable并不能獨(dú)立開啟一個(gè)線程,而是依賴Thread類去創(chuàng)建線程,執(zhí)行自己的run方法,去執(zhí)行相應(yīng)的業(yè)務(wù)邏輯,才能讓這個(gè)類具備多線程的特性。
使用繼承Thread方式和實(shí)現(xiàn)Runable接口方式分別創(chuàng)建子線程
使用繼承Thread類方式創(chuàng)建子線程
public?class?Main?extends?Thread{????public?static?void?main(String[]?args)?{????????Main?main?=?new?Main();????????main.start();????}????@Override????public?void?run()?{????????System.out.println("通過繼承Thread接口方式創(chuàng)建子線程成功,當(dāng)前線程名:"+Thread.currentThread().getName());????}}運(yùn)行結(jié)果:
使用實(shí)現(xiàn)Runnable接口方式創(chuàng)建子線程
public?class?Main{????public?static?void?main(String[]?args)?{????????SubThread?subThread?=?new?SubThread();????????Thread?thread?=?new?Thread(subThread);????????thread.start();????}}class?SubThread?implements?Runnable{????@Override????public?void?run()?{????????//?TODO?Auto-generated?method?stub????????System.out.println("通過實(shí)現(xiàn)Runnable接口創(chuàng)建子線程成功,當(dāng)前線程名:"+Thread.currentThread().getName());????}}運(yùn)行結(jié)果:
使用匿名內(nèi)部類方式創(chuàng)建子線程
public?class?Main{????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????//?TODO?Auto-generated?method?stub????????????????System.out.println("使用匿名內(nèi)部類方式創(chuàng)建線程成功,當(dāng)前線程名:"+Thread.currentThread().getName());????????????}????????});????????thread.start();????}}運(yùn)行結(jié)果:
關(guān)系
如何實(shí)現(xiàn)處理多線程的返回值
通過剛才的學(xué)習(xí),我們知道多線程的邏輯需要放到run方法中去執(zhí)行,而run方法是沒有返回值的,那么遇到需要返回值的狀況就不好解決,那么如何實(shí)現(xiàn)子線程返回值呢?
主線程等待法
通過讓主線程等待,直到子線程運(yùn)行完畢為止。
實(shí)現(xiàn)方式:
public?class?Main{????static?String?str;????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????str="子線程執(zhí)行完畢";????????????}????????});????????thread.start();????????//如果子線程還未對str進(jìn)行賦值,則一直輪轉(zhuǎn)????????while(str==null)?{}????????System.out.println(str);????}}使用Thread中的join()方法
join()方法可以阻塞當(dāng)前線程以等待子線程處理完畢。
實(shí)現(xiàn)方式:
public?class?Main{????static?String?str;????public?static?void?main(String[]?args)?{????????Thread?thread?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????str="子線程執(zhí)行完畢";????????????}????????});????????thread.start();????????//如果子線程還未對str進(jìn)行賦值,則一直輪轉(zhuǎn)????????try?{????????????thread.join();????????}?catch?(InterruptedException?e)?{????????????//?TODO?Auto-generated?catch?block????????????e.printStackTrace();????????}????????System.out.println(str);????}}join方法能做到比主線程等待法更精準(zhǔn)的控制,但是join方法的控制粒度并不夠細(xì)。比如,我需要控制子線程將字符串賦一個(gè)特定的值時(shí),再執(zhí)行主線程,這種操作join方法是沒有辦法做到的。
通過Callable接口實(shí)現(xiàn):通過FutureTask或者線程池獲取
在JDK1.5之前,線程是沒有返回值的,通常程序猿需要獲取子線程返回值頗費(fèi)周折,現(xiàn)在Java有了自己的返回值線程,即實(shí)現(xiàn)了Callable接口的線程,執(zhí)行了實(shí)現(xiàn)Callable接口的線程之后,可以獲得一個(gè)Future對象,在該對象上調(diào)用一個(gè)get方法,就可以執(zhí)行子線程的邏輯并獲取返回的Object。
實(shí)現(xiàn)方式1(錯(cuò)誤示例):
public?class?Main?implements?Callable{????@Override????public?String?call()?throws?Exception?{????????//?TODO?Auto-generated?method?stub????????String?str?=?"我是帶返回值的子線程";????????return?str;????}????public?static?void?main(String[]?args)?{????????Main?main?=?new?Main();????????try?{????????????String?str?=?main.call();????????????System.out.println(str);????????}?catch?(Exception?e)?{????????????//?TODO?Auto-generated?catch?block????????????e.printStackTrace();????????}????}}運(yùn)行結(jié)果:
實(shí)現(xiàn)方式2(使用FutureTask):
public?class?Main?implements?Callable{????@Override????public?String?call()?throws?Exception?{????????//?TODO?Auto-generated?method?stub????????String?str?=?"我是帶返回值的子線程";????????return?str;????}????public?static?void?main(String[]?args)?{????????FutureTask?task?=?new?FutureTask(new?Main());????????new?Thread(task).start();????????try?{????????????if(!task.isDone())?{????????????????System.out.println("任務(wù)沒有執(zhí)行完成");????????????}????????????System.out.println("等待中...");????????????Thread.sleep(3000);????????????System.out.println(task.get());????????}?catch?(InterruptedException?|?ExecutionException?e)?{????????????//?TODO?Auto-generated?catch?block????????????e.printStackTrace();????????}????}}運(yùn)行結(jié)果:
實(shí)現(xiàn)方法3(使用線程池配合Future獲取):
public?class?Main?implements?Callable{????@Override????public?String?call()?throws?Exception?{????????//?TODO?Auto-generated?method?stub????????String?str?=?"我是帶返回值的子線程";????????return?str;????}????public?static?void?main(String[]?args)?throws?InterruptedException,?ExecutionException?{????????ExecutorService?newCacheThreadPool?=?Executors.newCachedThreadPool();?????????Future?future?=?newCacheThreadPool.submit(new?Main());????????if(!future.isDone())?{????????????System.out.println("線程尚未執(zhí)行結(jié)束");????????}????????System.out.println("等待中");????????Thread.sleep(300);????????System.out.println(future.get());????????newCacheThreadPool.shutdown();????}}運(yùn)行結(jié)果:
線程的狀態(tài)
Java線程主要分為以下六個(gè)狀態(tài):新建態(tài)(new),運(yùn)行態(tài)(Runnable),無限期等待(Waiting),限期等待(TimeWaiting),阻塞態(tài)(Blocked),結(jié)束(Terminated)。
新建(new)
新建態(tài)是線程處于已被創(chuàng)建但沒有被啟動的狀態(tài),在該狀態(tài)下的線程只是被創(chuàng)建出來了,但并沒有開始執(zhí)行其內(nèi)部邏輯。
運(yùn)行(Runnable)
運(yùn)行態(tài)分為Ready和Running,當(dāng)線程調(diào)用start方法后,并不會立即執(zhí)行,而是去爭奪CPU,當(dāng)線程沒有開始執(zhí)行時(shí),其狀態(tài)就是Ready,而當(dāng)線程獲取CPU時(shí)間片后,從Ready態(tài)轉(zhuǎn)為Running態(tài)。
等待(Waiting)
處于等待狀態(tài)的線程不會自動蘇醒,而只有等待被其它線程喚醒,在等待狀態(tài)中該線程不會被CPU分配時(shí)間,將一直被阻塞。以下操作會造成線程的等待:
鎖:https://juejin.im/post/5d8da403f265da5b5d203bf4
限期等待(TimeWaiting)
處于限期等待的線程,CPU同樣不會分配時(shí)間片,但存在于限期等待的線程無需被其它線程顯式喚醒,而是在等待時(shí)間結(jié)束后,系統(tǒng)自動喚醒。以下操作會造成線程限時(shí)等待:
阻塞(Blocked)
當(dāng)多個(gè)線程進(jìn)入同一塊共享區(qū)域時(shí),例如Synchronized塊、ReentrantLock控制的區(qū)域等,會去整奪鎖,成功獲取鎖的線程繼續(xù)往下執(zhí)行,而沒有獲取鎖的線程將進(jìn)入阻塞狀態(tài),等待獲取鎖。
結(jié)束(Terminated)
已終止線程的線程狀態(tài),線程已結(jié)束執(zhí)行。
Sleep和Wait的區(qū)別
Sleep和Wait者兩個(gè)方法都可以使線程進(jìn)入限期等待的狀態(tài),那么這兩個(gè)方法有什么區(qū)別呢?
測試代碼:
public?class?Main{????public?static?void?main(String[]?args)?{????????Thread?threadA?=?new?Thread(new?ThreadA());????????Thread?threadB?=?new?Thread(new?ThreadB());????????threadA.setName("threadA");????????threadB.setName("threadB");????????threadA.start();????????threadB.start();????}????public?static?synchronized?void?print()?{????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行Sleep");????????try?{????????????Thread.sleep(1000);????????}?catch?(InterruptedException?e)?{????????????//?TODO?Auto-generated?catch?block????????????e.printStackTrace();????????}????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行Wait");????????try?{????????????Main.class.wait(1000);????????}?catch?(InterruptedException?e)?{????????????//?TODO?Auto-generated?catch?block????????????e.printStackTrace();????????}????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行完畢");????}}class?ThreadA?implements?Runnable{????@Override????public?void?run()?{????????//?TODO?Auto-generated?method?stub????????Main.print();????}}class?ThreadB?implements?Runnable{????@Override????public?void?run()?{????????//?TODO?Auto-generated?method?stub????????Main.print();????}}執(zhí)行結(jié)果:
從上面的結(jié)果可以分析出:當(dāng)線程A執(zhí)行sleep后,等待一秒被喚醒后繼續(xù)持有鎖,執(zhí)行之后的代碼,而執(zhí)行wait之后,立即釋放了鎖,不僅讓出了CPU還讓出了鎖,而后線程B立即持有鎖開始執(zhí)行,和線程A執(zhí)行了同樣的步驟,當(dāng)線程B執(zhí)行wait方法之后,釋放鎖,然后線程A拿到鎖打印了第一個(gè)執(zhí)行完畢,然后線程B打印執(zhí)行完畢。
notify和notifyAll的區(qū)別
notify
notify可以喚醒一個(gè)處于等待狀態(tài)的線程,上代碼:
public?class?Main{????public?static?void?main(String[]?args)?{????????Object?lock?=?new?Object();????????Thread?threadA?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????synchronized?(lock)?{????????????????????try?{????????????????????????lock.wait();????????????????????}?catch?(InterruptedException?e)?{????????????????????????//?TODO?Auto-generated?catch?block????????????????????????e.printStackTrace();????????????????????}????????????????????print();????????????????}????????????}????????});????????Thread?threadB?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????synchronized?(lock)?{????????????????????print();????????????????????lock.notify();????????????????}????????????}????????});????????threadA.setName("threadA");????????threadB.setName("threadB");????????threadA.start();????????threadB.start();????}????public?static?void?print()?{????????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行print");????????????try?{????????????????Thread.sleep(1000);????????????}?catch?(InterruptedException?e)?{????????????????//?TODO?Auto-generated?catch?block????????????????e.printStackTrace();????????????}????????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行完畢");????}}執(zhí)行結(jié)果:
代碼解釋:線程A在開始執(zhí)行時(shí)立即調(diào)用wait進(jìn)入無限等待狀態(tài),如果沒有別的線程來喚醒它,它將一直等待下去,所以此時(shí)B持有鎖開始執(zhí)行,并且在執(zhí)行完畢時(shí)調(diào)用了notify方法,該方法可以喚醒wait狀態(tài)的A線程,于是A線程蘇醒,開始執(zhí)行剩下的代碼。
notifyAll
notifyAll可以用于喚醒所有等待的線程,使所有處于等待狀態(tài)的線程都變?yōu)閞eady狀態(tài),去重新爭奪鎖。
public?class?Main{????public?static?void?main(String[]?args)?{????????Object?lock?=?new?Object();????????Thread?threadA?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????synchronized?(lock)?{????????????????????try?{????????????????????????lock.wait();????????????????????}?catch?(InterruptedException?e)?{????????????????????????//?TODO?Auto-generated?catch?block????????????????????????e.printStackTrace();????????????????????}????????????????????print();????????????????}????????????}????????});????????Thread?threadB?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????synchronized?(lock)?{????????????????????print();????????????????????lock.notifyAll();????????????????}????????????}????????});????????threadA.setName("threadA");????????threadB.setName("threadB");????????threadA.start();????????threadB.start();????}????public?static?void?print()?{????????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行print");????????????try?{????????????????Thread.sleep(1000);????????????}?catch?(InterruptedException?e)?{????????????????//?TODO?Auto-generated?catch?block????????????????e.printStackTrace();????????????}????????????System.out.println("當(dāng)前線程:"+Thread.currentThread().getName()+"執(zhí)行完畢");????}}執(zhí)行結(jié)果:
要喚醒前一個(gè)例子中的線程A,不光notify方法可以做到,調(diào)用notifyAll方法同樣也可以做到,那么兩者有什么區(qū)別呢?
區(qū)別
要說清楚他們的區(qū)別,首先要簡單的說一下Java synchronized的一些原理,在openjdk中查看java的源碼可以看到,java對象中存在monitor鎖,monitor對象中包含鎖池和等待池。
鎖池,假設(shè)有多個(gè)對象進(jìn)入synchronized塊爭奪鎖,而此時(shí)已經(jīng)有一個(gè)對象獲取到了鎖,那么剩余爭奪鎖的對象將直接進(jìn)入鎖池中。
等待池,假設(shè)某個(gè)線程調(diào)用了對象的wait方法,那么這個(gè)線程將直接進(jìn)入等待池,而等待池中的對象不會去爭奪鎖,而是等待被喚醒。
下面可以說notify和notifyAll的區(qū)別了:
notifyAll會讓所有處于等待池中的線程全部進(jìn)入鎖池去爭奪鎖,而notify只會隨機(jī)讓其中一個(gè)線程去爭奪鎖。
yield方法
概念
????/**?????*?A?hint?to?the?scheduler?that?the?current?thread?is?willing?to?yield?????*?its?current?use?of?a?processor.?The?scheduler?is?free?to?ignore?this?????*?hint.?????*?????*??Yield?is?a?heuristic?attempt?to?improve?relative?progression?????*?between?threads?that?would?otherwise?over-utilise?a?CPU.?Its?use?????*?should?be?combined?with?detailed?profiling?and?benchmarking?to?????*?ensure?that?it?actually?has?the?desired?effect.?????*?????*?
?It?is?rarely?appropriate?to?use?this?method.?It?may?be?useful?????*?for?debugging?or?testing?purposes,?where?it?may?help?to?reproduce?????*?bugs?due?to?race?conditions.?It?may?also?be?useful?when?designing?????*?concurrency?control?constructs?such?as?the?ones?in?the?????*?{@link?java.util.concurrent.locks}?package.?????*/????public?static?native?void?yield();
yield源碼上有一段長長的注釋,其大意是說:當(dāng)前線程調(diào)用yield方法時(shí),會給當(dāng)前線程調(diào)度器一個(gè)暗示,當(dāng)前線程愿意讓出CPU的使用,但是它的作用應(yīng)結(jié)合詳細(xì)的分析和測試來確保已經(jīng)達(dá)到了預(yù)期的效果,因?yàn)檎{(diào)度器可能會無視這個(gè)暗示,使用這個(gè)方法是不那么合適的,或許在測試環(huán)境中使用它會比較好。
測試:
public?class?Main{????public?static?void?main(String[]?args)?{????????Thread?threadA?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????System.out.println("ThreadA正在執(zhí)行yield");????????????????Thread.yield();????????????????System.out.println("ThreadA執(zhí)行yield方法完成");????????????}????????});????????Thread?threadB?=?new?Thread(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????System.out.println("ThreadB正在執(zhí)行yield");????????????????Thread.yield();????????????????System.out.println("ThreadB執(zhí)行yield方法完成");????????????}????????});????????threadA.setName("threadA");????????threadB.setName("threadB");????????threadA.start();????????threadB.start();????}測試結(jié)果:
可以看出,存在不同的測試結(jié)果,這里選出兩張。
第一種結(jié)果:線程A執(zhí)行完yield方法,讓出cpu給線程B執(zhí)行。然后兩個(gè)線程繼續(xù)執(zhí)行剩下的代碼。
第二種結(jié)果:線程A執(zhí)行yield方法,讓出cpu給線程B執(zhí)行,但是線程B執(zhí)行yield方法后并沒有讓出cpu,而是繼續(xù)往下執(zhí)行,此時(shí)就是系統(tǒng)無視了這個(gè)暗示。
interrupt方法
中止線程
interrupt函數(shù)可以中斷一個(gè)線程,在interrupt之前,通常使用stop方法來終止一個(gè)線程,但是stop方法過于暴力,它的特點(diǎn)是,不論被中斷的線程之前處于一個(gè)什么樣的狀態(tài),都無條件中斷,這會導(dǎo)致被中斷的線程后續(xù)的一些清理工作無法順利完成,引發(fā)一些不必要的異常和隱患,還有可能引發(fā)數(shù)據(jù)不同步的問題。
溫柔的interrupt方法
interrupt方法的原理與stop方法相比就顯得溫柔的多,當(dāng)調(diào)用interrupt方法去終止一個(gè)線程時(shí),它并不會暴力地強(qiáng)制終止線程,而是通知這個(gè)線程應(yīng)該要被中斷了,和yield一樣,這也是一種暗示,至于是否應(yīng)該中斷,由被中斷的線程自己去決定。當(dāng)對一個(gè)線程調(diào)用interrupt方法時(shí):
線程池
線程池的引入是用來解決在日常開發(fā)的多線程開發(fā)中,如果開發(fā)者需要使用到非常多的線程,那么這些線程在被頻繁的創(chuàng)建和銷毀時(shí),會對系統(tǒng)造成一定的影響,有可能系統(tǒng)在創(chuàng)建和銷毀這些線程所耗費(fèi)的時(shí)間會比完成實(shí)際需求的時(shí)間還要長。
另外,在線程很多的狀況下,對線程的管理就形成了一個(gè)很大的問題,開發(fā)者通常要將注意力從功能上轉(zhuǎn)移到對雜亂無章的線程進(jìn)行管理上,這項(xiàng)動作實(shí)際上是非常耗費(fèi)精力的。
利用Executors創(chuàng)建不同的線程池滿足不同場景的需求
newFixThreadPool(int nThreads)指定工作線程數(shù)量的線程池。
newCachedThreadPool()處理大量中斷事件工作任務(wù)的線程池,
newSingleThreadExecutor()創(chuàng)建唯一的工作線程來執(zhí)行任務(wù),如果線程異常結(jié)束,會有另一個(gè)線程取代它。可保證順序執(zhí)行任務(wù)。
newSingleThreadScheduledExecutor()與newScheduledThreadPool(int corePoolSize)定時(shí)或周期性工作調(diào)度,兩者的區(qū)別在于前者是單一工作線程,后者是多線程
newWorkStealingPool()內(nèi)部構(gòu)建ForkJoinPool,利用working-stealing算法,并行地處理任務(wù),不保證處理順序。
Fork/Join框架:把大任務(wù)分割稱若干個(gè)小任務(wù)并行執(zhí)行,最終匯總每個(gè)小任務(wù)后得到大任務(wù)結(jié)果的框架。
為什么要使用線程池
線程是稀缺資源,如果無限制地創(chuàng)建線程,會消耗系統(tǒng)資源,而線程池可以代替開發(fā)者管理線程,一個(gè)線程在結(jié)束運(yùn)行后,不會銷毀線程,而是將線程歸還線程池,由線程池再進(jìn)行管理,這樣就可以對線程進(jìn)行復(fù)用。
所以線程池不但可以降低資源的消耗,還可以提高線程的可管理性。
使用線程池啟動線程
public?class?Main{????public?static?void?main(String[]?args)?{????????ExecutorService?newFixThreadPool?=?Executors.newFixedThreadPool(10);????????newFixThreadPool.execute(new?Runnable()?{????????????@Override????????????public?void?run()?{????????????????//?TODO?Auto-generated?method?stub????????????????System.out.println("通過線程池啟動線程成功");????????????}????????});????????newFixThreadPool.shutdown();????}}新任務(wù)execute執(zhí)行后的判斷
要知道這個(gè)點(diǎn)首先要先說說ThreadPoolExecutor的構(gòu)造函數(shù),其中有幾個(gè)參數(shù):
那么新任務(wù)提交后會執(zhí)行下列判斷:
handler 線程池飽和策略
- AbortPolicy:直接拋出異常,默認(rèn)。
- CallerRunsPolicy:用調(diào)用者所在的線程來執(zhí)行任務(wù)。
- DiscardOldestPolicy:丟棄隊(duì)列中靠最前的任務(wù),并執(zhí)行當(dāng)前任務(wù)。
- DiscardPolicy:直接丟棄任務(wù)
- 自定義。
線程池的大小如何選定
這個(gè)問題并不是什么秘密,在網(wǎng)上各大技術(shù)網(wǎng)站均有文章說明,我就拿一個(gè)最受認(rèn)可的寫上吧
- CPU密集型:線程數(shù) = 核心數(shù)或者核心數(shù)+1
- IO密集型:線程數(shù) = CPU核數(shù)*(1+平均等待時(shí)間/平均工作時(shí)間)
當(dāng)然這個(gè)也不能完全依賴這個(gè)公式,更多的是要依賴平時(shí)的經(jīng)驗(yàn)來操作,這個(gè)公式也只是僅供參考而已。
結(jié)語
本文提供了一些Java多線程和并發(fā)方面最最基礎(chǔ)的知識,適合初學(xué)者了解Java多線程的一些基本知識,如果想了解更多的關(guān)于并發(fā)方面的內(nèi)容可以看:
https://juejin.im/post/5d8da403f265da5b5d203bf4
總結(jié)
以上是生活随笔為你收集整理的主线程如何等待多线程完成 返回数据_多线程基础体系知识清单的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pep8 python 编码规范_Pyt
- 下一篇: tensorflow gpu利用率为0_