java創(chuàng)建線程的4種方式集合(最新整理)
在Java中,創(chuàng)建線程有多種方式,每種方式都有其適用場(chǎng)景和優(yōu)缺點(diǎn)。以下是四種常見(jiàn)的創(chuàng)建線程的方式:
一、方式一:繼承Thread類(lèi)
這是最直觀的方式,通過(guò)創(chuàng)建一個(gè)新的類(lèi)繼承Thread
類(lèi),并重寫(xiě)其run
方法。
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("數(shù)據(jù):" + i); } } } public class Demo01 { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); MyThread thread2 = new MyThread(); thread2.start(); for (int i = 10; i < 100; i++) { System.err.println("Main:" + i); } } }
優(yōu)點(diǎn):簡(jiǎn)單直觀。
缺點(diǎn):Java不支持多重繼承,因此如果一個(gè)類(lèi)已經(jīng)繼承了另一個(gè)類(lèi),那么它就不能再繼承
Thread
類(lèi)。
二、方式二:實(shí)現(xiàn)Runnable接口創(chuàng)建線程目標(biāo)類(lèi)
這種方式更加靈活,因?yàn)樗试S類(lèi)繼承其他類(lèi)的同時(shí)實(shí)現(xiàn)Runnable
接口。
class A implements Runnable { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } public class Demo03 { public static void main(String[] args) { A a = new A(); new Thread(a).start(); new Thread(() -> { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } }).start(); for (int i = 0; i < 1000; i++) { System.err.println(Thread.currentThread().getName() + ":" + i); } } }
優(yōu)點(diǎn):避免了Java單繼承的限制,可以更容易地?cái)U(kuò)展功能。
缺點(diǎn):需要手動(dòng)管理線程的生命周期。
三、使用Callable和FutureTask創(chuàng)建線程
Callable
接口與Runnable
類(lèi)似,但它可以返回值并拋出異常。
class MyCall implements Callable<Integer> { @Override public Integer call() throws Exception { return 200; } } public class Demo08 { public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<Integer> futureTask = new FutureTask<>(new MyCall()); new Thread(futureTask, "計(jì)算線程").start(); Integer i = futureTask.get(); System.out.println(i); } }
優(yōu)點(diǎn):可以返回值和拋出異常。
缺點(diǎn):使用起來(lái)比
Runnable
復(fù)雜,需要配合FutureTask
使用。
通過(guò)FutureTask類(lèi)和Callable接口的聯(lián)合使用可以創(chuàng)建能夠獲取異步執(zhí)行結(jié)果的線程,具體步驟如下:
(1) 創(chuàng)建一個(gè)Callable接口的實(shí)現(xiàn)類(lèi),并實(shí)現(xiàn)其call()方法,編寫(xiě)好異步執(zhí)行的具體邏輯,可以有返回值。
(2) 使用Callable實(shí)現(xiàn)類(lèi)的實(shí)例構(gòu)造一個(gè)FutureTask實(shí)例。
(3) 使用FutureTask實(shí)例作為T(mén)hread構(gòu)造器的target入?yún)?,?gòu)造新的Thread線程實(shí)例。
(4) 調(diào)用Thread實(shí)例的start()方法啟動(dòng)新線程,啟動(dòng)新線程的run()方法并發(fā)執(zhí)行。其內(nèi)部的執(zhí)行過(guò)程為:?jiǎn)?dòng)Thread實(shí)例的run()方法并發(fā)執(zhí)行后,會(huì)執(zhí)行FutureTask實(shí)例的run()方法,最終會(huì)并發(fā)執(zhí)行Callable實(shí)現(xiàn)類(lèi)的call()方法。
(5) 調(diào)用FutureTask對(duì)象的get()方法阻塞性地獲得并發(fā)線程的執(zhí)行結(jié)果。
按照以上步驟,通過(guò)Callable接口和Future接口相結(jié)合創(chuàng)建多線程,實(shí)例如下:
創(chuàng)建一個(gè)Callable接口的實(shí)現(xiàn)類(lèi):
public class CallableTaskDemo implements Callable { // 編寫(xiě)好異步執(zhí)行的具體邏輯,可以有返回值。 // (Runnable接口中的run()方法是沒(méi)有返回值得,Callable接口的call()方法有返回值) @Override public Long call() throws Exception { Long startTime = System.currentTimeMillis(); System.out.println(Thread.currentThread().getName()+" 線程開(kāi)始運(yùn)行"); Thread.sleep(1000); for(int i=0;i<100000000;i++){ int j = i*10000; } Long endTime = System.currentTimeMillis(); Long used = endTime-startTime; System.out.println(Thread.currentThread().getName()+" 線程結(jié)束運(yùn)行"); return used; } }
在這個(gè)例子中有兩個(gè)線程:一個(gè)是執(zhí)行main()方法的主線程,叫作main;另一個(gè)是main線程通過(guò)thread.start()方法啟動(dòng)的業(yè)務(wù)線程,叫作callableTaskThread。該線程是一個(gè)包含F(xiàn)utureTask任務(wù)作為target的Thread線程。
public class CreateDemo { public static void main(String[] args) throws InterruptedException { CallableTaskDemo callableTaskDemo = new CallableTaskDemo(); FutureTask<Long> futureTask = new FutureTask<Long>(callableTaskDemo); Thread thread = new Thread(futureTask,"callableTaskThread"); thread.start(); Thread.sleep(500); System.out.println("main線程執(zhí)行一會(huì)"); for(int i=0;i<100000000/2;i++){ int j = i*10000; } // 獲取并發(fā)任務(wù)的執(zhí)行結(jié)果 try { System.out.println(thread.getName()+" 線程占用時(shí)間:"+futureTask.get()); } catch (ExecutionException e) { e.printStackTrace(); } } }
main線程通過(guò)thread.start()啟動(dòng)callableTaskThread線程之后,會(huì)繼續(xù)自己的事情,callableTaskThread線程開(kāi)始并發(fā)執(zhí)行。
callableTaskThread線程首先執(zhí)行的是thread.run()方法,然后在其中會(huì)執(zhí)行到其target(futureTask任務(wù))的run()方法;接著在這個(gè)futureTask.run()方法中會(huì)執(zhí)行futureTask的callable成員的call()方法,這里的callable成員(ReturnableTask實(shí)例)是通過(guò)FutureTask構(gòu)造器在初始化時(shí)傳遞進(jìn)來(lái)的、自定義的Callable實(shí)現(xiàn)類(lèi)的實(shí)例。
說(shuō)明:
Callable 接口:Callable 是一個(gè)泛型接口,call() 方法中定義了線程的任務(wù),并返回一個(gè)結(jié)果。
Future 接口:Future 表示一個(gè)異步計(jì)算的結(jié)果,使用 get() 方法可以獲取線程執(zhí)行的結(jié)果。
ExecutorService:通常與 ExecutorService 結(jié)合使用,可以更好地管理線程池和任務(wù)。
優(yōu)點(diǎn):
返回結(jié)果:Callable 可以返回執(zhí)行結(jié)果,而 Runnable 不能。
異常處理:Callable 可以拋出受檢查的異常,便于處理復(fù)雜任務(wù)中的錯(cuò)誤。
線程池支持:與 ExecutorService 結(jié)合,可以實(shí)現(xiàn)靈活的線程管理。
缺點(diǎn):
相對(duì)復(fù)雜:相比 Runnable 和 Thread,Callable 和 Future 的使用稍微復(fù)雜一些,特別是涉及到線程池的管理。
四、使用線程池
線程池是一種更加高效的方式來(lái)管理線程,它可以復(fù)用線程,減少創(chuàng)建和銷(xiāo)毀線程的開(kāi)銷(xiāo)。
第一種:使用自帶的API實(shí)現(xiàn)
Java中的線程池是通過(guò)Executor框架實(shí)現(xiàn)的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個(gè)類(lèi)。
經(jīng)常使用的線程池做法:
1、Executors.newFixedThreadPool(int)執(zhí)行長(zhǎng)期任務(wù)性能好,創(chuàng)建一個(gè)線程池,一池有N個(gè)固定的線程,有固定線程數(shù)的線程
newFixedThreadPool創(chuàng)建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue
2、Executors.newSingleThreadExecutor()
一個(gè)任務(wù)一個(gè)任務(wù)的執(zhí)行,一池一線程。
newSingleThreadExecutor 創(chuàng)建的線程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue
3、Executors.newCachedThreadPool()
執(zhí)行很多短期異步任務(wù),線程池根據(jù)需要?jiǎng)?chuàng)建新線程,但在先前構(gòu)建的線程可用時(shí)將重用它們??蓴U(kuò)容,遇強(qiáng)則強(qiáng)。
newCachedThreadPool創(chuàng)建的線程池將corePoolSize設(shè)置為0,將maximumPoolSize設(shè)置為Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是說(shuō)來(lái)了任務(wù)就創(chuàng)建線程運(yùn)行,當(dāng)線程空閑超過(guò)60秒,就銷(xiāo)毀線程。
代碼演示:
import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyThreadPoolDemo { public static void main(String[] args) { // ExecutorService threadPool = Executors.newFixedThreadPool(5); //一個(gè)銀行網(wǎng)點(diǎn),5個(gè)受理業(yè)務(wù)的窗口 // ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一個(gè)銀行網(wǎng)點(diǎn),1個(gè)受理業(yè)務(wù)的窗口 ExecutorService threadPool = Executors.newCachedThreadPool(); //一個(gè)銀行網(wǎng)點(diǎn),可擴(kuò)展受理業(yè)務(wù)的窗口 //10個(gè)顧客請(qǐng)求 try { for (int i = 1; i <=10; i++) { threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"\t 辦理業(yè)務(wù)"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
第二種:使用自定義線程池(面試的重點(diǎn),不要輕視)
1、corePoolSize:線程池中的常駐核心線程數(shù)
2、maximumPoolSize:線程池中能夠容納同時(shí)執(zhí)行的最大線程數(shù),此值必須大于等于1
3、keepAliveTime:多余的空閑線程的存活時(shí)間
當(dāng)前池中線程數(shù)量超過(guò)corePoolSize時(shí),當(dāng)空閑時(shí)間達(dá)到keepAliveTime時(shí), 多余線程會(huì)被銷(xiāo)毀直到只剩下corePoolSize個(gè)線程為止。
4、unit:keepAliveTime的單位
5、workQueue:任務(wù)隊(duì)列,被提交但尚未被執(zhí)行的任務(wù) 就是我們之前講的阻塞隊(duì)列
6、threadFactory:表示生成線程池中工作線程的線程工廠,用于創(chuàng)建線程,一般默認(rèn)的即可
7、handler:拒絕策略,表示當(dāng)隊(duì)列滿了,并且工作線程大于等于線程池的最大線程數(shù)(maximumPoolSize)時(shí)如何來(lái)拒絕。
請(qǐng)求執(zhí)行的runnable的策略
隊(duì)列和阻塞隊(duì)列?
阻塞隊(duì)列: 假如這個(gè)隊(duì)列中沒(méi)有數(shù)據(jù),會(huì)阻塞獲取數(shù)據(jù)的一方,如果隊(duì)列滿了,會(huì)阻塞存儲(chǔ)數(shù)據(jù)的一方。
線程池的拒絕策略:
AbortPolicy(默認(rèn)):直接拋出RejectedExecutionException異常阻止系統(tǒng)正常運(yùn)行。
CallerRunsPolicy:“調(diào)用者運(yùn)行”一種調(diào)節(jié)機(jī)制,該策略既不會(huì)拋棄任務(wù),也不會(huì)拋出異常,而是將某些任務(wù)回退到調(diào)用者,俗稱(chēng)從哪兒來(lái)到哪兒去。
DiscardOldestPolicy:拋棄隊(duì)列中等待最久的任務(wù),然后把當(dāng)前任務(wù)加人隊(duì)列中嘗試再次提交當(dāng)前任務(wù)。
DiscardPolicy:該策略默默地丟棄無(wú)法處理的任務(wù),不予任何處理也不拋出異常。如果允許任務(wù)丟失,這是最好的一種策略。
以上內(nèi)置拒絕策略均實(shí)現(xiàn)了RejectedExecutionHandle接口
1、在創(chuàng)建了線程池后,線程池中的線程數(shù)為零。
2、當(dāng)調(diào)用execute()方法添加一個(gè)請(qǐng)求任務(wù)時(shí),線程池會(huì)做出如下判斷:
2.1如果正在運(yùn)行的線程數(shù)量小于corePoolSize,那么馬上創(chuàng)建線程運(yùn)行這個(gè)任務(wù);
2.2如果正在運(yùn)行的線程數(shù)量大于或等于corePoolSize,那么將這個(gè)任務(wù)放入隊(duì)列;
2.3如果這個(gè)時(shí)候隊(duì)列滿了且正在運(yùn)行的線程數(shù)量還小于maximumPoolSize,那么還是要?jiǎng)?chuàng)建非核心線程立刻運(yùn)行這個(gè)任務(wù);
2.4如果隊(duì)列滿了且正在運(yùn)行的線程數(shù)量大于或等于maximumPoolSize,那么線程池會(huì)啟動(dòng)飽和拒絕策略來(lái)執(zhí)行。
3、當(dāng)一個(gè)線程完成任務(wù)時(shí),它會(huì)從隊(duì)列中取下一個(gè)任務(wù)來(lái)執(zhí)行。
4、當(dāng)一個(gè)線程無(wú)事可做超過(guò)一定的時(shí)間(keepAliveTime)時(shí),線程會(huì)判斷:
如果當(dāng)前運(yùn)行的線程數(shù)大于corePoolSize,那么這個(gè)線程就被停掉。
所以線程池的所有任務(wù)完成后,它最終會(huì)收縮到corePoolSize的大小。
代碼演示:
import java.util.Arrays; import java.util.List; import java.util.concurrent.*; /** * 線程池 * Arrays * Collections * Executors */ public class MyThreadPoolDemo { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 2L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), //new ThreadPoolExecutor.AbortPolicy() //new ThreadPoolExecutor.CallerRunsPolicy() //new ThreadPoolExecutor.DiscardOldestPolicy() new ThreadPoolExecutor.DiscardOldestPolicy() ); //10個(gè)顧客請(qǐng)求 try { for (int i = 1; i <= 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 辦理業(yè)務(wù)"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
總結(jié):每種創(chuàng)建線程的方式都有其適用場(chǎng)景。在實(shí)際開(kāi)發(fā)中,我們通常會(huì)根據(jù)需求選擇最合適的方式。例如,對(duì)于簡(jiǎn)單的后臺(tái)任務(wù),可以使用
Runnable
接口;對(duì)于需要返回結(jié)果的任務(wù),可以使用Callable
接口;而對(duì)于需要頻繁創(chuàng)建和銷(xiāo)毀線程的場(chǎng)景,使用線程池是更好的選擇。
到此這篇關(guān)于java創(chuàng)建線程的4種方式的文章就介紹到這了,更多相關(guān)java創(chuàng)建線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義View實(shí)現(xiàn)支付寶咻一咻效果
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)支付寶咻一咻效果的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02android studio實(shí)現(xiàn)計(jì)算器
這篇文章主要為大家詳細(xì)介紹了android studio實(shí)現(xiàn)計(jì)算器的具體方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07android使用AsyncTask實(shí)現(xiàn)多線程下載實(shí)例
這篇文章主要介紹了android使用AsyncTask實(shí)現(xiàn)多線程下載實(shí)例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02新版Android studio導(dǎo)入微信支付和支付寶官方Demo問(wèn)題解決大全
這篇文章主要為大家詳細(xì)介紹了新版Android studio導(dǎo)入微信支付和支付寶官方Demo問(wèn)題的解決大全,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07淺談android獲取設(shè)備唯一標(biāo)識(shí)完美解決方案
本篇文章主要介紹了淺談android獲取設(shè)備唯一標(biāo)識(shí)完美解決方案,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-085個(gè)Android開(kāi)發(fā)中比較常見(jiàn)的內(nèi)存泄漏問(wèn)題及解決辦法
本文主要介紹了5個(gè)Android開(kāi)發(fā)中比較常見(jiàn)的內(nèi)存泄漏問(wèn)題及解決辦法,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02Android使用GridView實(shí)現(xiàn)橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用GridView實(shí)現(xiàn)橫向滾動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07