目錄
二、多線(xiàn)程的實(shí)現(xiàn)方式(四種)
三、線(xiàn)程池的創(chuàng)建使用(五種)
1、newFixedThreadPool定長(zhǎng)線(xiàn)程池
2、CachedThreadPool可緩存線(xiàn)程池
3、newSingleThreadExecutor單線(xiàn)程化線(xiàn)程池
4、newScheduledThreadPool周期性線(xiàn)程池
無(wú)返回值的周期性線(xiàn)程池
有返回值的周期性線(xiàn)程池
5、ThreadPoolExecutor(手動(dòng)創(chuàng)建線(xiàn)程池)
線(xiàn)程池的submit和execute方法區(qū)別
一、初認(rèn)多線(xiàn)程
1、什么是線(xiàn)程
進(jìn)程是:一個(gè)應(yīng)用程序(1個(gè)進(jìn)程是一個(gè)軟件)。
線(xiàn)程是:一個(gè)進(jìn)程中的執(zhí)行場(chǎng)景/執(zhí)行單元。
注意: 一個(gè)進(jìn)程可以啟動(dòng)多個(gè)線(xiàn)程。
java主線(xiàn)程: 每個(gè)java程序都含有一個(gè)線(xiàn)程,那就是主線(xiàn)程(main線(xiàn)程)。Java應(yīng)用程序都是從主類(lèi)main方法執(zhí)行的,當(dāng)jvm加載代碼,發(fā)現(xiàn)賣(mài)弄方法之后,就會(huì)啟動(dòng)一個(gè)線(xiàn)程,這個(gè)線(xiàn)程就是主線(xiàn)程,負(fù)責(zé)執(zhí)行main方法。如果在主線(xiàn)程里面創(chuàng)建其他線(xiàn)程,就會(huì)在主線(xiàn)程和其他線(xiàn)程來(lái)回切換,直到其他所有線(xiàn)程結(jié)束才會(huì)結(jié)束主線(xiàn)程。
所謂多線(xiàn)程,就是說(shuō)一個(gè)應(yīng)用程序有多條執(zhí)行路徑,每當(dāng)我們打開(kāi)一個(gè)應(yīng)用程序的時(shí)候,就相打開(kāi)了一個(gè)進(jìn)程,而進(jìn)程中執(zhí)行的操作(這就是一條線(xiàn)程對(duì)應(yīng)用程序進(jìn)行訪(fǎng)問(wèn)),就是線(xiàn)程。以迅雷為例,打開(kāi)迅雷就相當(dāng)于打開(kāi)一個(gè)進(jìn)程,下載文件的操作就是線(xiàn)程,多線(xiàn)程就是同時(shí)下載多個(gè)文件。
以接口為例,當(dāng)許多人同時(shí)調(diào)用一個(gè)接口,我們就可以把每一個(gè)人看做一條線(xiàn)程去調(diào)用我們的接口。
二、多線(xiàn)程的實(shí)現(xiàn)方式(四種)
1、繼承 Thread 類(lèi)
通過(guò)繼承 Thread 類(lèi)似實(shí)現(xiàn)多線(xiàn)程的步驟如下:
public class MyThread extends Thread { @Override public void run() { System.out.println(“我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫” + Thread.currentThread().getName()); }}class TestMyThread { public static void main(String[] args) { MyThread myThread1 = new MyThread(); myThread1.setName(“Thread-1”); MyThread myThread2 = new MyThread(); myThread2.setName(“Thread-2”); MyThread myThread3 = new MyThread(); myThread3.setName(“Thread-3”); myThread1.start(); myThread2.start(); myThread3.start(); }}
為了演示線(xiàn)程執(zhí)行順序的隨機(jī)性,我特意創(chuàng)建了三個(gè)線(xiàn)程,并為每一個(gè)線(xiàn)程命名,下面是我運(yùn)行五次程序的執(zhí)行結(jié)果:
// 第一次我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-2我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-3// 第二次我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-3我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-2// 第三次我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-3我是通過(guò)繼承 Thread 類(lèi)創(chuàng)建的多線(xiàn)程,我叫Thread-2
從上面的執(zhí)行結(jié)果我們可以看到線(xiàn)程的執(zhí)行順序和代碼中編寫(xiě)的順序沒(méi)有關(guān)系,線(xiàn)程的執(zhí)行順序是具有隨機(jī)性的。
2、實(shí)現(xiàn) Runnable 接口
通過(guò)實(shí)現(xiàn) Runnable 接口實(shí)現(xiàn)多線(xiàn)程的步驟如下:
1.創(chuàng)建 MyRunnable 類(lèi)實(shí)現(xiàn) Runnable 接口。
2.創(chuàng)建 MyRunnable 類(lèi)的實(shí)例對(duì)象 myRunnable 。
3.把實(shí)例對(duì)象 myRunnable 作為參數(shù)來(lái)創(chuàng)建 Thread 類(lèi)的實(shí)例對(duì)象 thread,實(shí)例對(duì)象 thread 就是一個(gè)新線(xiàn)程。
4.調(diào)用 start() 方法,啟動(dòng)線(xiàn)程。、
public class RunnableTest implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫" + Thread.currentThread().getName()); } } } class Test { public static void main(String[] args) { Thread myThread1 = new Thread(new RunnableTest()); Thread myThread2 = new Thread(new RunnableTest()); myThread1.start(); myThread2.start(); }}
執(zhí)行結(jié)果如下:
我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-1我是通過(guò)實(shí)現(xiàn) Runnable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0
注:
相比于繼承 Thread 類(lèi)的方法來(lái)說(shuō),實(shí)現(xiàn) Runnable 接口是一個(gè)更好地選擇,因?yàn)?Java 不支持多繼承,但是可以實(shí)現(xiàn)多個(gè)接口。
有一點(diǎn)值得注意的是 Thread 類(lèi)也實(shí)現(xiàn)了 Runnable 接口,這意味著構(gòu)造函數(shù) Thread(Runnable target) 不僅可以傳入 Runnable 接口的對(duì)象,而且可以傳入一個(gè) Thread 類(lèi)的對(duì)象,這樣就可以將一個(gè) Thread 對(duì)象中的 run() 方法交由其他線(xiàn)程進(jìn)行調(diào)用。
3、實(shí)現(xiàn) Callable 接口
Callable 接口只有一個(gè) call() 方法,源碼如下:
public interface Callable { V call() throws Exception;}
從源碼我們可以看到 Callable 接口和 Runnable 接口類(lèi)似,它們之間的區(qū)別在于 run() 方法沒(méi)有返回值,而 call() 方法是有返回值的。
通過(guò)實(shí)現(xiàn) Callable 接口實(shí)現(xiàn)多線(xiàn)程的步驟如下:
1.創(chuàng)建 MyCallable 類(lèi)實(shí)現(xiàn) Callable 接口。
2.創(chuàng)建 MyCallable 類(lèi)的實(shí)例對(duì)象 myCallable。
3.把實(shí)例對(duì)象 myCallable 作為參數(shù)來(lái)創(chuàng)建 FutureTask 類(lèi)的實(shí)例對(duì)象 futureTask。
4.把實(shí)例對(duì)象 futureTask 作為參數(shù)來(lái)創(chuàng)建 Thread 類(lèi)的實(shí)例對(duì)象 thread,實(shí)例對(duì)象 thread 就是一個(gè)新線(xiàn)程。
5.調(diào)用 start() 方法,啟動(dòng)線(xiàn)程。
public class CallbaleTest implements Callable { @Override public Integer call() throws Exception { int a = 6; int b = 9; System.out.println(“我是通過(guò)實(shí)現(xiàn) Callable 接口創(chuàng)建的多線(xiàn)程,我叫” + Thread.currentThread().getName()); return a + b; } } class TestMyCallable { public static void main(String[] args) throws ExecutionException, InterruptedException { CallbaleTest myCallable = new CallbaleTest(); FutureTask futureTask = new FutureTask(myCallable); Thread thread = new Thread(futureTask); Thread thread1 = new Thread(futureTask); thread.start(); thread1.start(); Integer integer = futureTask.get(); System.out.println(“返回值為:” + integer); } }
執(zhí)行后的結(jié)果如下:
我是通過(guò)實(shí)現(xiàn) Callable 接口創(chuàng)建的多線(xiàn)程,我叫Thread-0返回值為:15
注:FutureTask 類(lèi)提供了一個(gè) get() 方法用來(lái)獲取 call() 方法的返回值,但需要注意的是調(diào)用這個(gè)方法會(huì)導(dǎo)致程序阻塞,必須要等到線(xiàn)程結(jié)束后才會(huì)得到返回值。
4、線(xiàn)程池(下面講)
三、線(xiàn)程池的創(chuàng)建使用(五種)
上面講的是通過(guò)new Thread等方式創(chuàng)建線(xiàn)程,這種方式的弊端是:
a. 每次new Thread新建對(duì)象性能差。
b. 線(xiàn)程缺乏統(tǒng)一管理,可能無(wú)限制新建線(xiàn)程,相互之間競(jìng)爭(zhēng),及可能占用過(guò)多系統(tǒng)資源導(dǎo)致死機(jī)或oom。
c. 缺乏更多功能,如定時(shí)執(zhí)行、定期執(zhí)行、線(xiàn)程中斷。
下面將要介紹的是Jdk提供的四種線(xiàn)程池的好處在于:
a. 重用存在的線(xiàn)程,減少對(duì)象創(chuàng)建、消亡的開(kāi)銷(xiāo),性能佳。
b. 可有效控制最大并發(fā)線(xiàn)程數(shù),提高系統(tǒng)資源的使用率,同時(shí)避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞。
c. 提供定時(shí)執(zhí)行、定期執(zhí)行、單線(xiàn)程、并發(fā)數(shù)控制等功能。
1、newFixedThreadPool定長(zhǎng)線(xiàn)程池
Executors.newFixedThreadPool:創(chuàng)建一個(gè)固定大小的線(xiàn)程池,可控制并發(fā)的線(xiàn)程數(shù),超出的線(xiàn)程會(huì)在隊(duì)列中等待。
import java.util.concurrent.*;public class Test { public static void main(String[] args) { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2); for (int j = 0; j { for (int i = 0; i < 2; i++) { System.out.println("線(xiàn)程名:" + Thread.currentThread().getName() + " i是:" + i); } }); } fixedThreadPool.shutdown();//關(guān)閉線(xiàn)程池 //shutdownNow();//停止接收新任務(wù),原來(lái)的任務(wù)停止執(zhí)行,但是它并不對(duì)正在執(zhí)行的任務(wù)做任何保證,有可能它們都會(huì)停止,也有可能執(zhí)行完成。 }}
輸出:
線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-2 i是:0線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-2 i是:1
2、CachedThreadPool可緩存線(xiàn)程池
Executors.newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線(xiàn)程池,若線(xiàn)程數(shù)超過(guò)處理所需,緩存一段時(shí)間后會(huì)回收,若線(xiàn)程數(shù)不夠,則新建線(xiàn)程。
可緩存線(xiàn)程池為無(wú)限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)回收復(fù)用第一個(gè)任務(wù)的線(xiàn)程,而不用每次新建線(xiàn)程,可靈活回收空閑線(xiàn)程,若無(wú)可回收,則新建線(xiàn)程。
public class Test { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int j = 0; j { for (int i = 0; i < 2; i++) { System.out.println("線(xiàn)程名:" + Thread.currentThread().getName() + " i是:" + i); } }); } cachedThreadPool.shutdown();//關(guān)閉線(xiàn)程池 }}
輸出:
線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-3 i是:0線(xiàn)程名:pool-1-thread-2 i是:0線(xiàn)程名:pool-1-thread-2 i是:1線(xiàn)程名:pool-1-thread-3 i是:1
使用場(chǎng)景:
CachedThreadPool 是根據(jù)短時(shí)間的任務(wù)量來(lái)決定創(chuàng)建的線(xiàn)程數(shù)量的,所以它適合短時(shí)間內(nèi)有突發(fā)大量任務(wù)的處理場(chǎng)景。
3、newSingleThreadExecutor單線(xiàn)程化線(xiàn)程池
newSingleThreadExecutor線(xiàn)程池你可以理解為特殊的newFixedThreadPool線(xiàn)程池,它只會(huì)創(chuàng)建一個(gè)線(xiàn)程,并且所有任務(wù)按照指定順序。如果你創(chuàng)建了多個(gè)任務(wù),因?yàn)橹粫?huì)有一個(gè)線(xiàn)程,多余的任務(wù)會(huì)被阻塞到隊(duì)列里依次執(zhí)行。
下面的示例循環(huán)3次,每次都是用的一個(gè)線(xiàn)程,這個(gè)線(xiàn)程會(huì)先執(zhí)行第一個(gè)循環(huán)的任務(wù),在執(zhí)行第二個(gè)循環(huán)的任務(wù),再執(zhí)行第三個(gè)循環(huán)的任務(wù),所以輸出的 i 是有序的。
public class newSingleThreadExecutor { public static void main(String[] args) { ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); for (int j = 0; j { for (int i = 0; i < 3; i++) { System.out.println("線(xiàn)程名:" + Thread.currentThread().getName() + " i是:" + i); } }); } System.out.println("準(zhǔn)備關(guān)閉線(xiàn)程池"); singleThreadPool.shutdown();//關(guān)閉線(xiàn)程池 } }
輸出:
準(zhǔn)備關(guān)閉線(xiàn)程池線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-1 i是:2線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-1 i是:2線(xiàn)程名:pool-1-thread-1 i是:0線(xiàn)程名:pool-1-thread-1 i是:1線(xiàn)程名:pool-1-thread-1 i是:2
單個(gè)線(xiàn)程的線(xiàn)程池有什么意義?
單個(gè)線(xiàn)程的線(xiàn)程池相比于線(xiàn)程來(lái)說(shuō),它的優(yōu)點(diǎn)有以下 2 個(gè):
可以復(fù)用線(xiàn)程:即使是單個(gè)線(xiàn)程池,也可以復(fù)用線(xiàn)程。
提供了任務(wù)管理功能:?jiǎn)蝹€(gè)線(xiàn)程池也擁有任務(wù)隊(duì)列,在任務(wù)隊(duì)列可以存儲(chǔ)多個(gè)任務(wù),這是線(xiàn)程無(wú)法實(shí)現(xiàn)的,并且當(dāng)任務(wù)隊(duì)列滿(mǎn)了之后,可以執(zhí)行拒絕策略,這些都是線(xiàn)程不具備的。
4、newScheduledThreadPool周期性線(xiàn)程池
周期性線(xiàn)程池用來(lái)處理延時(shí)任務(wù)或定時(shí)任務(wù)。
無(wú)返回值的周期性線(xiàn)程池
public class newScheduledThreadPool { public static void main(String[] args) { ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3); System.out.println(“測(cè)試1”); for (int i = 0; i { System.out.println(“線(xiàn)程名:” + Thread.currentThread().getName() + “已經(jīng)過(guò)了3秒”); }, 3, TimeUnit.SECONDS); } System.out.println(“測(cè)試2”); scheduleThreadPool.shutdown();//關(guān)閉線(xiàn)程池 } }
說(shuō)明:
我們聲明了3個(gè)線(xiàn)程,創(chuàng)建的時(shí)候用循環(huán)創(chuàng)建了5個(gè),多出來(lái)的2個(gè)會(huì)阻塞直到前3個(gè)線(xiàn)程有執(zhí)行完的再?gòu)?fù)用他們的線(xiàn)程;因?yàn)椴捎昧搜訒r(shí)3秒輸出,所以會(huì)先輸出測(cè)試1、測(cè)試2,然后等待3秒后再執(zhí)行輸出線(xiàn)程的內(nèi)容。
輸出:
測(cè)試1測(cè)試2線(xiàn)程名:pool-1-thread-2已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-3已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-1已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-3已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-2已經(jīng)過(guò)了3秒
有返回值的周期性線(xiàn)程池
public static void main(String[] args) { ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3); System.out.println(“測(cè)試1”); ScheduledFuture scheduledFuture = scheduleThreadPool.schedule(() -> { return “線(xiàn)程名:” + Thread.currentThread().getName() + “已經(jīng)過(guò)了3秒”; }, 3, TimeUnit.SECONDS); System.out.println(“測(cè)試2”); try { //獲取線(xiàn)程返回的值并輸出 System.out.println(scheduledFuture.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } scheduleThreadPool.shutdown();//關(guān)閉線(xiàn)程池 } }
輸出:
測(cè)試1測(cè)試2線(xiàn)程名:pool-1-thread-1已經(jīng)過(guò)了3秒
定時(shí)線(xiàn)程執(zhí)行
定時(shí)執(zhí)行可以用scheduleAtFixedRate方法進(jìn)行操作,里面的參數(shù)4表示代碼或啟動(dòng)運(yùn)行后第4秒開(kāi)始執(zhí)行,3表示每3秒執(zhí)行一次。因?yàn)槲覀冊(cè)O(shè)置了3個(gè)線(xiàn)程,所以運(yùn)行后線(xiàn)程會(huì)在第4秒開(kāi)始用3個(gè)線(xiàn)程每3秒執(zhí)行一次。
public static void main(String[] args) { ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3); System.out.println(“測(cè)試1”); scheduleThreadPool.scheduleAtFixedRate(() -> { System.out.println(“線(xiàn)程名:” + Thread.currentThread().getName() + “已經(jīng)過(guò)了3秒”); }, 4, 3, TimeUnit.SECONDS); System.out.println(“測(cè)試2”); }
輸出:
測(cè)試1測(cè)試2線(xiàn)程名:pool-1-thread-1已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-1已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-2已經(jīng)過(guò)了3秒線(xiàn)程名:pool-1-thread-3已經(jīng)過(guò)了3秒……
5、ThreadPoolExecutor(手動(dòng)創(chuàng)建線(xiàn)程池)
上面我們介紹了四種JDK自帶的線(xiàn)程池,但是平常不推薦使用。
ThreadPoolExecutor 相比于其他創(chuàng)建線(xiàn)程池的優(yōu)勢(shì)在于,它可以通過(guò)參數(shù)來(lái)控制最大任務(wù)數(shù)和拒絕策略,讓線(xiàn)程池的執(zhí)行更加透明和可控,所以在阿里巴巴《Java開(kāi)發(fā)手冊(cè)》是這樣規(guī)定的:
【強(qiáng)制要求】線(xiàn)程池不允許使用 Executors 去創(chuàng)建,而是通過(guò) ThreadPoolExecutor 的方式,這樣的處理方式讓寫(xiě)的同學(xué)更加明確線(xiàn)程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
這一方面是由于jdk中自帶的線(xiàn)程池,都有其局限性,不夠靈活;另外使用ThreadPoolExecutor有助于大家明確線(xiàn)程池的運(yùn)行規(guī)則,創(chuàng)建符合自己的業(yè)務(wù)場(chǎng)景需要的線(xiàn)程池,避免資源耗盡的風(fēng)險(xiǎn)。
需要進(jìn)行線(xiàn)程池的初始化,所以引入以下依賴(lài):
com.google.guava guava 29.0-jre
在開(kāi)始前需要注意線(xiàn)程池的幾個(gè)參數(shù):
(在下面代碼的ThreadPoolExecutor里你會(huì)看到這些參數(shù)):
corePoolSize=> 線(xiàn)程池里的核心線(xiàn)程數(shù)量maximumPoolSize=> 線(xiàn)程池里允許有的最大線(xiàn)程數(shù)量keepAliveTime=> 空閑線(xiàn)程存活時(shí)間unit=> keepAliveTime的時(shí)間單位,比如分鐘,小時(shí)等workQueue=> 緩沖隊(duì)列threadFactory=> 線(xiàn)程工廠用來(lái)創(chuàng)建新的線(xiàn)程放入線(xiàn)程池handler=> 線(xiàn)程池拒絕任務(wù)的處理策略,比如拋出異常等策略線(xiàn)程池按以下行為執(zhí)行任務(wù) 1. 當(dāng)線(xiàn)程數(shù)小于核心線(xiàn)程數(shù)時(shí),創(chuàng)建線(xiàn)程。 2. 當(dāng)線(xiàn)程數(shù)大于等于核心線(xiàn)程數(shù),且任務(wù)隊(duì)列未滿(mǎn)時(shí),將任務(wù)放入任務(wù)隊(duì)列。 3. 當(dāng)線(xiàn)程數(shù)大于等于核心線(xiàn)程數(shù),且任務(wù)隊(duì)列已滿(mǎn) -1 若線(xiàn)程數(shù)小于最大線(xiàn)程數(shù),創(chuàng)建線(xiàn)程 -2 若線(xiàn)程數(shù)等于最大線(xiàn)程數(shù),拋出異常,拒絕任務(wù)
無(wú)返回值的線(xiàn)程創(chuàng)建
代碼初始化了線(xiàn)程池并用 executorService.execute 分別創(chuàng)建了兩個(gè)線(xiàn)程,一個(gè)用來(lái)輸出本線(xiàn)程的名字,另一個(gè)用來(lái)異步調(diào)用 printA() 方法。
public static void main(String[] args) { System.out.println(“開(kāi)始”); //線(xiàn)程池的初始化 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(“demo-pool-%d”).build(); ExecutorService executorService = new ThreadPoolExecutor( 60, 100, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); //開(kāi)啟一個(gè)新線(xiàn)程用來(lái)輸出線(xiàn)程的名字 executorService.execute(() -> System.out.println(“第1個(gè)線(xiàn)程名字” + Thread.currentThread().getName())); //再開(kāi)啟一個(gè)新線(xiàn)執(zhí)行printA() executorService.execute(() -> { System.out.println(“第2個(gè)線(xiàn)程名字” + Thread.currentThread().getName()); printA(); }); System.out.println(“完成”); executorService.shutdown(); } public static void printA() { for (int i = 0; i < 3; i++) { System.out.println("打?。篴aaaaaaaaaaaa"); } }
輸出:
開(kāi)始完成第1個(gè)線(xiàn)程名字demo-pool-0第2個(gè)線(xiàn)程名字demo-pool-1打印:aaaaaaaaaaaaa打?。篴aaaaaaaaaaaa打印:aaaaaaaaaaaaa
有返回值的多線(xiàn)程調(diào)用
使用submit
public static void main(String[] args) { System.out.println(“開(kāi)始”); //線(xiàn)程池的初始化 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat(“demo-pool-%d”).build(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); //異步調(diào)用對(duì)象integerCallableTask中的call()計(jì)算1-100的和 Future future = threadPoolExecutor.submit(() -> { int nummber = 100; int sum = 0; for (int i = 0; i <= nummber; i++) { sum += i; } return sum; }); try { //獲取計(jì)算的結(jié)果 Integer result = future.get(); System.out.println("和是:" + result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("完成"); //shutdown():停止接收新任務(wù),原來(lái)的任務(wù)繼續(xù)執(zhí)行 //shutdownNow():停止接收新任務(wù),原來(lái)的任務(wù)停止執(zhí)行 threadPoolExecutor.shutdown(); }
輸出:
開(kāi)始和是:5050完成
線(xiàn)程池的submit和execute方法區(qū)別
1、接收的參數(shù)不一樣
execute接收的參數(shù)是new Runnable(),重寫(xiě)run()方法,是沒(méi)有返回值的:
源碼:
public interface Executor { /** * Executes the given command at some time in the future. The command * may execute in a new thread, in a pooled thread, or in the calling * thread, at the discretion of the {@code Executor} implementation. * * @param command the runnable task * @throws RejectedExecutionException if this task cannot be * accepted for execution * @throws NullPointerException if command is null */ void execute(Runnable command);}
submit接收的參數(shù)是Callable,重寫(xiě)call()方法,是有返回值的:
源碼:
/** * @throws RejectedExecutionException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public Future submit(Callable task) { if (task == null) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task); execute(ftask); return ftask; }