簡介
常見線程池種類
種類 |
核心線程數 |
最大線程數 |
描述 |
SingleThreadPool |
1 |
1 |
只一個線程在工作,相當於單線程順序串行執行所有任務。 |
FixedThreadPool |
存在 |
無(雖然有但實際不起作用) |
|
CachedThreadPool |
0 |
無限大 |
動態增刪線程數 |
ScheduleThreadPool |
存在 |
無限大 |
週期性執行任務 |
常用方法
方法 |
作用 |
說明 |
ThreadPoolExecutor參數 |
newFixed ThreadPool |
創建固定大小的線程池。 |
每提交一個任務就創建一個線程,直到線程數到線程池的最大數 線程池的大小一旦達到最大值就會保持不變。 若某線程因異常而結束,線程池會補充一個新線程。 |
nThreads, nThreads, //nThreads是傳進來的參數 |
newSingle ThreadExecutor |
創建一個單線程的線程池。
|
只一個線程在工作,相當於單線程串行執行所有任務。 若這個唯一的線程因爲異常結束,會有一個新的線程來替代它。 此線程池保證所有任務的執行順序按照任務的提交順序執行。 |
1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() |
newSingleThread ScheduledExecutor |
創建一個單線程的定時線程池。 |
此線程池支持定時以及週期性執行任務的需求。 |
return new DelegatedScheduledExecutorService (newScheduledThreadPoolExecutor(1)); |
newCached ThreadPool |
創建一個可緩存的線程池。
|
若線程池的數量超過了處理任務所需要的線程,就回收部分空閒(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。 此線程池大小爲Integer的最大值。 |
0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>() |
newScheduled ThreadPool |
創建一個固定大小的定時線程池。 |
此線程池支持定時以及週期性執行任務的需求。 |
corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, //corePoolSize是傳進來的參數 |
這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者Callable對象代表的線程。
FixedThreadPool
簡介
FixedThreadPool的execute流程
參考網址:線程池的幾種實現方式 - 簡書
對上圖的說明如下。
1)如果當前運行的線程數少於corePoolSize(即線程池中無運行的線程),則創建一個新線程來執行任務。
2)在線程池完線程的創建之後,將任務加入Linked- BlockingQueue。
3)線程會在一個無限循環中反覆從LinkedBlockingQueue獲取任務來執行。
FixedThreadPool使用無界隊列LinkedBlockingQueue作爲線程池的工作隊列(隊列的容量爲 Integer.MAX_VALUE)。使用無界隊列作爲工作隊列會對線程池帶來如下影響:
1)線程池中的線程數達到corePoolSize後,新任務將在無界隊列中等待,因此線程池中的線程數不會超過corePoolSize。
2)由於1,使用無界隊列時maximumPoolSize將是一個無效參數。
3)由於1和2,使用無界隊列時keepAliveTime將是一個無效參數。
4)由於使用無界隊列,運行中的FixedThreadPool(未執行方法shutdown()或 shutdownNow())不會拒絕任務(不會調用RejectedExecutionHandler.rejectedExecution方法)。
實例
package org.example.a;
import java.util.concurrent.*;
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + "正在執行task "+taskNum);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "-------------------執行task "+taskNum + " 完畢!!!");
}
}
public class Demo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for(int i=0;i<6;i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
}
executor.shutdown();
}
}
執行結果
pool-1-thread-2 正在執行task 1
pool-1-thread-3 正在執行task 2
pool-1-thread-1 正在執行task 0
pool-1-thread-3 -------------------執行task 2 完畢!!!
pool-1-thread-3 正在執行task 3
pool-1-thread-2 -------------------執行task 1 完畢!!!
pool-1-thread-2 正在執行task 4
pool-1-thread-1 -------------------執行task 0 完畢!!!
pool-1-thread-1 正在執行task 5
pool-1-thread-1 -------------------執行task 5 完畢!!!
pool-1-thread-3 -------------------執行task 3 完畢!!!
pool-1-thread-2 -------------------執行task 4 完畢!!!
SingleThreadPool
簡介
SingleThreadPool的execute流程
參考網址:線程池的幾種實現方式 - 簡書
對上圖的說明如下。
1)如果當前運行的線程數少於corePoolSize(即線程池中無運行的線程),則創建一個新線程來執行任務。
2)在線程池完線程的創建之後,將任務加入Linked- BlockingQueue。
3)線程會在一個無限循環中反覆從LinkedBlockingQueue獲取任務來執行。
實例
package org.example.a;
import java.util.concurrent.*;
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " " + "正在執行task "+taskNum);
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + "-------------------執行task "+taskNum + " 完畢!!!");
}
}
public class Demo {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
for(int i=0;i<6;i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
}
executor.shutdown();
}
}
執行結果
pool-1-thread-1 正在執行task 0
pool-1-thread-1 -------------------執行task 0 完畢!!!
pool-1-thread-1 正在執行task 1
pool-1-thread-1 -------------------執行task 1 完畢!!!
pool-1-thread-1 正在執行task 2
pool-1-thread-1 -------------------執行task 2 完畢!!!
pool-1-thread-1 正在執行task 3
pool-1-thread-1 -------------------執行task 3 完畢!!!
pool-1-thread-1 正在執行task 4
pool-1-thread-1 -------------------執行task 4 完畢!!!
pool-1-thread-1 正在執行task 5
pool-1-thread-1 -------------------執行task 5 完畢!!!
CachedThreadPool
簡介
CachedThreadPool的execute流程
參考網址:線程池的幾種實現方式 - 簡書
對上圖的說明如下。
首先執行SynchronousQueue.offer(Runnable task)。如果當前maximumPool中有空閒線程正在執行
SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那麼主線程執行offer操作與空閒線程執行的poll操作配對成功,主線程把任務交給空閒線程執行,execute()方法執行完成;否則執行下面的步驟當初始maximumPool爲空,或者maximumPool中當前沒有空閒線程時,將沒有線程執行
SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。這種情況下,步驟(1)將失敗。此時CachedThreadPool會創建一個新線程執行任務,execute()方法執行完成。在步驟(2)中新創建的線程將任務執行完後,會執行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。這個poll操作會讓空閒線程最多在SynchronousQueue中等待60秒鐘。如果60秒鐘內主線程提交了一個新任務(主線程執行步驟(1)),那麼這個空閒線程將執行主線程提交的新任務;否則,這個空閒線程將終止。由於空閒60秒的空閒線程會被終止,因此長時間保持空閒的CachedThreadPool不會使用任何資源。
前面提到過,SynchronousQueue是一個沒有容量的阻塞隊列。每個插入操作必須等待另一個線程的對應移除操作,反之亦然。CachedThreadPool使用SynchronousQueue,把主線程提交的任務傳遞給空閒線程執行。CachedThreadPool中任務傳遞的示意圖如圖所示。
CachedThreadPool是一個會根據需要創建新線程的線程池。下面是創建CachedThreadPool的源代碼。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue());
}
CachedThreadPool的corePoolSize被設置爲0,即corePool爲空;maximumPoolSize被設置爲 Integer.MAX_VALUE,即maximumPool是無界的。這裏把keepAliveTime設置爲60L,意味着 CachedThreadPool中的空閒線程等待新任務的最長時間爲60秒,空閒線程超過60秒後將會被終止。
FixedThreadPool和SingleThreadExecutor使用無界隊列LinkedBlockingQueue作爲線程池的工作隊列。CachedThreadPool使用沒有容量的SynchronousQueue作爲線程池的工作隊列,但 CachedThreadPool的maximumPool是無界的。這意味着,如果主線程提交任務的速度高於 maximumPool中線程處理任務的速度時,CachedThreadPool會不斷創建新線程。極端情況下,
CachedThreadPool會因爲創建過多線程而耗盡CPU和內存資源。
SingleScheduledExecutorService
簡介
其他網址
ScheduledExecutorService的scheduleAtFixedRate和scheduleWithFixedDelay方法的區別_18875541的博客-CSDN博客
另見:《Java併發編程之美》=> 第9章 Java 併發包中ScheduledThreadPoolExecutor 原理探究
方法 | 說明 | 備註 |
scheduleAtFixedRate | 以上一次任務的開始時間爲間隔的,並且當任務執行時間大於設置的間隔時間時,真正間隔的時間由任務執行時間爲準 | scheduleAtFixedRate和scheduleWithFixedRate第2個參數:延時多長時間之後運行第一次任務 |
scheduleWithFixedDelay | 以上次任務的結束時間爲間隔,即:上一個任務結束後,延時定義的時間間隔,然後再次執行。 |
實例
程序1:(間隔時間(3s)大於任務的執行時間(1s))
package org.example.a;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
class MyTask implements Runnable {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(sdf.format(new Date()) + " " + "正在執行task");
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------------執行task " + " 完畢!!!");
}
}
public class Demo {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(new MyTask(), 0, 3, TimeUnit.SECONDS);
// executor.scheduleWithFixedRate(new MyTask(), 0, 3, TimeUnit.SECONDS);
//不能加這句
//executor.shutdown();
}
}
運行結果
scheduleAtFixedRate:可見,每3秒運行一次
22:54:15 正在執行task -------------------執行task 完畢!!! 22:54:18 正在執行task -------------------執行task 完畢!!! 22:54:21 正在執行task -------------------執行task 完畢!!! 22:54:24 正在執行task -------------------執行task 完畢!!! 22:54:27 正在執行task -------------------執行task 完畢!!! 22:54:30 正在執行task -------------------執行task 完畢!!! 22:54:33 正在執行task -------------------執行task 完畢!!!
scheduleWithFixedRate:可見,每4秒運行一次
23:04:08 正在執行task -------------------執行task 完畢!!! 23:04:12 正在執行task -------------------執行task 完畢!!! 23:04:16 正在執行task -------------------執行task 完畢!!! 23:04:20 正在執行task -------------------執行task 完畢!!! 23:04:24 正在執行task -------------------執行task 完畢!!! 23:04:28 正在執行task
程序2 :(間隔時間(3s)小於任務的執行時間(4s))
package org.example.a;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
class MyTask implements Runnable {
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(sdf.format(new Date()) + " " + "正在執行task");
Thread.currentThread().sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------------執行task " + " 完畢!!!");
}
}
public class Demo {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedDelay(new MyTask(), 0, 3, TimeUnit.SECONDS);
//executor.scheduleWithFixedDelay(new MyTask(), 0, 3, TimeUnit.SECONDS);
//這句不要寫
//executor.shutdown();
}
}
運行結果
scheduleAtFixedRate:可見,此時間隔時間失效,會以任務執行時間爲準。上一個任務一結束就開始下一個任務
22:58:53 正在執行task -------------------執行task 完畢!!! 22:58:57 正在執行task -------------------執行task 完畢!!! 22:59:01 正在執行task -------------------執行task 完畢!!! 22:59:05 正在執行task -------------------執行task 完畢!!! 22:59:09 正在執行task -------------------執行task 完畢!!!
scheduleWithFixedRate:
23:08:31 正在執行task -------------------執行task 完畢!!! 23:08:38 正在執行task -------------------執行task 完畢!!! 23:08:45 正在執行task -------------------執行task 完畢!!! 23:08:52 正在執行task -------------------執行task 完畢!!! 23:08:59 正在執行task -------------------執行task 完畢!!!
異常處理
若程序中捕獲了異常,則不影響下一個任務執行,若不捕獲,則會影響下一個任務執行。
程序1:不捕獲異常
package org.example.a;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
class MyTask implements Runnable {
int i = 0;
int[] a = {1, 2, 3};
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(sdf.format(new Date()) + " " + "正在執行task");
System.out.println(a[i++]);
System.out.println("-------------------執行task " + " 完畢!!!");
}
}
public class Demo {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(new MyTask(), 0, 1, TimeUnit.SECONDS);
//這句不要寫
//executor.shutdown();
}
}
運行結果(異常後,直接卡死)
23:15:28 正在執行task
1
-------------------執行task 完畢!!!
23:15:29 正在執行task
2
-------------------執行task 完畢!!!
23:15:30 正在執行task
3
-------------------執行task 完畢!!!
23:15:31 正在執行task
程序2:捕獲異常
package org.example.a;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
class MyTask implements Runnable {
int i = 0;
int[] a = {1, 2, 3};
@Override
public void run() {
try {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
System.out.println(sdf.format(new Date()) + " " + "正在執行task");
System.out.println(a[i++]);
System.out.println("-------------------執行task " + " 完畢!!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Demo {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleWithFixedDelay(new MyTask(), 0, 1, TimeUnit.SECONDS);
//這句不要寫
//executor.shutdown();
}
}
執行結果(一直往下執行)
23:17:10 正在執行task
1
-------------------執行task 完畢!!!
23:17:11 正在執行task
2
-------------------執行task 完畢!!!
23:17:12 正在執行task
3
-------------------執行task 完畢!!!
23:17:13 正在執行task
java.lang.ArrayIndexOutOfBoundsException: 3
at org.example.a.MyTask.run(Demo.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
23:17:14 正在執行task
java.lang.ArrayIndexOutOfBoundsException: 4
at org.example.a.MyTask.run(Demo.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
23:17:15 正在執行task
java.lang.ArrayIndexOutOfBoundsException: 5
at org.example.a.MyTask.run(Demo.java:15)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)