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