Java四种线程创建的思路
一、哪四種
傳統(tǒng)的是繼承thread類和實(shí)現(xiàn)runnable接口,java5以后又有實(shí)現(xiàn)callable接口和java的線程池獲得。 callable相比于runnable,多了返回值,拋出了異常。
二、繼承Thread類創(chuàng)建線程
Thread類本質(zhì)上是實(shí)現(xiàn)了Runnable接口的一個(gè)實(shí)例,代表一個(gè)線程的實(shí)例。啟動(dòng)線程的唯一方法就是通過Thread類的start()實(shí)例方法。start()方法是一個(gè)native方法,它將啟動(dòng)一個(gè)新線程,并執(zhí)行run()方法。我們可以通過覆蓋run()方法達(dá)到自定義。
class MyThread extends Thread {@Overridepublic void run() {System.out.println("MyThread.run()");}public static void main(String[] args) throws InterruptedException {MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();myThread1.start();myThread2.start();} }步驟:
1.繼承 Thread 類 2.覆蓋 run() 方法 3.直接調(diào)用 Thread.start() 執(zhí)行三、實(shí)現(xiàn)Runnable接口創(chuàng)建線程
Runnable是一個(gè)函數(shù)式接口,如果自己的類已經(jīng)extends另一個(gè)類,無法直接extends Thread,此時(shí),可以實(shí)現(xiàn)一個(gè)Runnable接口。
public class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("this is runnable");}public static void main(String[] args) throws InterruptedException {MyRunnable myRunnable = new MyRunnable();MyRunnable myRunnable1 = new MyRunnable();Thread thread=new Thread(myRunnable);Thread thread1=new Thread(myRunnable1);thread.start();thread1.start();thread.join();thread1.join();} }步驟:
1.實(shí)現(xiàn)Runnable接口 2.獲取實(shí)現(xiàn)Runnable接口的實(shí)例,作為參數(shù),創(chuàng)建Thread實(shí)例 3.執(zhí)行 Thread#start() 啟動(dòng)線程四、實(shí)現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建Thread線程
Callable也是函數(shù)式接口,相比于Runnable接口而言,會(huì)有個(gè)返回值
class callAble implements Callable<Integer> {@Overridepublic Integer call() throws Exception {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+"---Callable.call()");return 200;} } public class MyCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> ft2 = new FutureTask<Integer>(new callAble());FutureTask<Integer> ft =new FutureTask<Integer>(()->{TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName()+"---Callable.call()");return 1024;});new Thread(ft,"zhang3").start();new Thread(ft2,"li4").start();System.out.println(Thread.currentThread().getName());System.out.println(ft.get());System.out.println(ft2.get());} } //main //zhang3---Callable.call() //li4---Callable.call() //1024 //200步驟
1.實(shí)現(xiàn)Callable接口 2.以Callable的實(shí)現(xiàn)類為參數(shù),創(chuàng)建FutureTask實(shí)例//搭建runnable與callable之間的橋梁 3.將FutureTask作為Thread的參數(shù),創(chuàng)建Thread實(shí)例 4.通過 Thread#start 啟動(dòng)線程 5.通過 FutreTask#get() 阻塞獲取線程的返回值五、runnable與callable的異同
Callable接口和Runnable接口相似,區(qū)別就是Callable需要實(shí)現(xiàn)call方法,而Runnable需要實(shí)現(xiàn)run方法;并且,call方法還可以返回任何對象,無論是什么對象,JVM都會(huì)當(dāng)作Object來處理。但是如果使用了泛型,我們就不用每次都對Object進(jìn)行轉(zhuǎn)換了。
Runnable.run()
Callable.call()
不同之處:
1.Callable可以返回一個(gè)類型V,而Runnable不可以
2.Callable能夠拋出checked exception,而Runnable不可以。
3.Runnable是自從java1.1就有了,而Callable是1.5之后才加上去的
4.Callable和Runnable都可以應(yīng)用于executors。而Thread類只支持Runnable.
上面只是簡單的不同,其實(shí)這兩個(gè)接口在用起來差別還是很大的。Callable與executors聯(lián)合在一起,在任務(wù)完成時(shí)可立刻獲得一個(gè)更新了的Future。而Runable卻要自己處理。
六、關(guān)于FutureTask接口
FutureTask接口,一般都是取回Callable執(zhí)行的狀態(tài)用的。其中的主要方法:
與callable相關(guān)的構(gòu)造方法:
使用FutrueTask的情景:
- 在主線程中需要執(zhí)行比較耗時(shí)的操作時(shí),但又不想阻塞主線程時(shí),可以把這些作業(yè)交給Future對象在后臺(tái)完成,
當(dāng)主線程將來需要時(shí),就可以通過Future對象獲得后臺(tái)作業(yè)的計(jì)算結(jié)果或者執(zhí)行狀態(tài)。 - 一般FutureTask多用于耗時(shí)的計(jì)算,主線程可以在完成自己的任務(wù)后,再去獲取結(jié)果。
- 僅在計(jì)算完成時(shí)才能檢索結(jié)果;如果計(jì)算尚未完成,則阻塞 get 方法。一旦計(jì)算完成,就不能再重新開始或取消計(jì)算。get方法而獲取結(jié)果只有在計(jì)算完成時(shí)獲取,否則會(huì)一直阻塞直到任務(wù)轉(zhuǎn)入完成狀態(tài),
然后會(huì)返回結(jié)果或者拋出異常。 - 只計(jì)算一次,get方法放到最后,可以使用isDone方法,判斷是否計(jì)算完,再獲取
runnable不關(guān)心返回,只管執(zhí)行,也不用告訴我完成沒有,我不care,您自己隨便玩,所以一般使用就是new Thread(new Runnable() { public void run() {...} }).start()換成JDK8的 lambda表達(dá)式就更簡單了 new Thread(() -> {}).start();
callbale就悲催一點(diǎn),沒法隨意了,必須等待返回的結(jié)果,但是這個(gè)線程的狀態(tài)我又控制不了,怎么辦?只能借助FutrueTask(相同的操作只會(huì)執(zhí)行一次,以后的線程再調(diào)用,直接返回),所以一般可以看到使用方式如下:
七、使用線程池創(chuàng)建線程(三大方法,七大參數(shù),四大策略)
線程池的優(yōu)勢:
線程池做的工作只要是控制運(yùn)行的線程數(shù)量,處理過程中將任務(wù)放入隊(duì)列,然后在線程創(chuàng)建后啟動(dòng)這些任務(wù),如果線程數(shù)量超過了最大數(shù)量,超出數(shù)量的線程排隊(duì)等候,等其他線程執(zhí)行完畢,再從隊(duì)列中取出任務(wù)來執(zhí)行。
它的主要特點(diǎn)為:線程復(fù)用;控制最大并發(fā)數(shù);管理線程。
- 第一:降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的銷耗。
- 第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行。
- 第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會(huì)銷耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
Java中的線程池是通過Executor框架實(shí)現(xiàn)的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個(gè)類。
八、線程池底層工作原理
- 1、在創(chuàng)建了線程池后,線程池中的線程數(shù)為零。
- 2、當(dāng)調(diào)用execute()方法添加一個(gè)請求任務(wù)時(shí),線程池會(huì)做出如下判斷:
- 3、當(dāng)一個(gè)線程完成任務(wù)時(shí),它會(huì)從隊(duì)列中取下一個(gè)任務(wù)來執(zhí)行。
- 4、當(dāng)一個(gè)線程無事可做超過一定的時(shí)間(keepAliveTime)時(shí),線程會(huì)判斷:
如果當(dāng)前運(yùn)行的線程數(shù)大于corePoolSize,那么這個(gè)線程就被停掉。
所以線程池的所有任務(wù)完成后,它最終會(huì)收縮到corePoolSize的大小。
newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
九、Java通過Executors提供的線程池
- newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool 創(chuàng)建一個(gè)定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。
- newSingleThreadExecutor創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。
- newScheduledThreadPool 創(chuàng)建一個(gè)定長線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
可以看出,上面的類都使用ThreadPoolExecutor實(shí)現(xiàn):
//主要參數(shù):- 1、corePoolSize:線程池中的常駐核心線程數(shù)- 2、maximumPoolSize:線程池中能夠容納同時(shí) 執(zhí)行的最大線程數(shù),此值必須大于等于1- 3、keepAliveTime:多余的空閑線程的存活時(shí)間 當(dāng)前池中線程數(shù)量超過corePoolSize時(shí),當(dāng)空閑時(shí)間達(dá)到keepAliveTime時(shí),多余線程會(huì)被銷毀直到 只剩下corePoolSize個(gè)線程為止- 4、unit:keepAliveTime的單位- 5、workQueue:任務(wù)隊(duì)列,被提交但尚未被執(zhí)行的任務(wù)- 6、threadFactory:表示生成線程池中工作線程的線程工廠, 用于創(chuàng)建線程,一般默認(rèn)的即可- 7、handler:拒絕策略,表示當(dāng)隊(duì)列滿了,并且工作線程大于 等于線程池的最大線程數(shù)(maximumPoolSize)時(shí)如何來拒絕請求執(zhí)行的runnable的策略 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)十、線程池的拒絕策略
- 是什么
等待隊(duì)列已經(jīng)排滿了,再也塞不下新任務(wù)了。同時(shí),線程池中的max線程也達(dá)到了,無法繼續(xù)為新任務(wù)服務(wù)。這個(gè)是時(shí)候我們就需要拒絕策略機(jī)制合理的處理這個(gè)問題。 - JDK內(nèi)置的拒絕策略
ThreadPoolExecutor 有四個(gè)構(gòu)造方法,實(shí)際上我們一般常用第一個(gè)構(gòu)造方法,它默認(rèn)指定AbortPolicy ()為拒絕策略
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);} public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);} public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);} public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}- 工作中用哪個(gè)線程池
一個(gè)都不用,我們工作中只能使用自定義的
如下代碼:
總結(jié)
以上是生活随笔為你收集整理的Java四种线程创建的思路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年快手大健康行业数据价值报告
- 下一篇: Java基本数据类型及String类