1.new Thread的弊端
執行一個異步任務你還只是如下new Thread嗎?
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
那你就out太多了,new Thread的弊端如下:
- 每次new Thread新建對象性能差。
- 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能佔用過多系統資源導致死機或oom。
- 缺乏更多功能,如定時執行、定期執行、線程中斷。
相比new Thread,Java提供的四種線程池的好處在於:
- 重用存在的線程,減少對象創建、消亡的開銷,性能佳。
- 可有效控制最大併發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
- 提供定時執行、定期執行、單線程、併發數控制等功能。
2.Java 自帶線程池
Java通過Executors提供四種線程池,分別爲:
- newCachedThreadPool創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
- newFixedThreadPool 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
- newScheduledThreadPool 創建一個定長線程池,支持定時及週期性任務執行。
- newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
主要方法:
1.void execute(Runnable command):
在未來某個時間執行給定命令。該命令可能在新的線程、已入池的線程或者正調用的線程中執行,這由Executor決定。2.void shutdown():
啓動一次順序關閉,執行以前提交的任務,但不接受新任務。如果已經關閉,則調用沒有其它作用。
2.1.newCachedThreadPool
創建一個可緩存的線程池。必要的時候創建新線程來處理請求,也會重用線程池中已經處於可用狀態的線程。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程;當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
此類型線程池特別適合於耗時短,不需要考慮同步的場合。
測試代碼:
/**
* 可緩存線程池
*/
private static void testCachedThreadPool() {
System.out.println("這是可緩存線程池:");
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
print(index + "");
}
});
}
}
輸出結果:
這是可緩存線程池:
1464230153368: pool-1-thread-1: 0
1464230154372: pool-1-thread-1: 1
1464230156376: pool-1-thread-1: 2
1464230159380: pool-1-thread-1: 3
1464230163384: pool-1-thread-1: 4
1464230168386: pool-1-thread-1: 5
1464230174391: pool-1-thread-1: 6
1464230181395: pool-1-thread-1: 7
1464230189397: pool-1-thread-1: 8
1464230198400: pool-1-thread-1: 9
2.2.newFixedThreadPool
創建固定大小的線程池,以無界隊列方式運行。線程池滿且線程都爲活動狀態的時候如果有新任務提交進來,它們會等待直到有線程可用。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。顯式調用shutdown將關閉線程池。
此類型線程池比較符合常用場合。
測試代碼:
/**
* 定長線程池
*/
private static void testFixedThreadPool() {
System.out.println("這是定長線程池:");
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
print(index + "");
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
輸出結果:
這是定長線程池:
1464229952132: pool-1-thread-1: 0
1464229952133: pool-1-thread-2: 1
1464229952133: pool-1-thread-3: 2
1464229953136: pool-1-thread-3: 3
1464229953136: pool-1-thread-1: 4
1464229953136: pool-1-thread-2: 5
1464229954138: pool-1-thread-1: 6
1464229954138: pool-1-thread-2: 8
1464229954138: pool-1-thread-3: 7
1464229955139: pool-1-thread-1: 9
2.3.newScheduledThreadPool
創建可定時運行(初始延時),運行頻率(每隔多長時間運行,還是運行成功一次之後再隔多長時間再運行)的線程池。
此類型線程池適合定時以及週期性執行任務的場合
特別注意:
- 初始延遲2秒後,每間隔3秒運行一次線程:
schedulePool.scheduleAtFixedRate(new MyThread(), 2, 3, TimeUnit.SECONDS);- 初始延遲2秒後,每運行成功後再等3秒運行一次線程:
schedulePool.scheduleWithFixedDelay(new MyThread(), 2, 3, TimeUnit.SECONDS);
測試代碼:
/**
* 週期線程池
*/
private static void testScheduledThreadPool() {
System.out.println("這是週期線程池:");
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
print("delay 3 seconds");
}
}, 2, 3, TimeUnit.SECONDS);
}
輸出結果:
這是週期線程池:
1464231196776: pool-1-thread-1: delay 3 seconds
1464231199776: pool-1-thread-1: delay 3 seconds
1464231202775: pool-1-thread-2: delay 3 seconds
1464231205777: pool-1-thread-1: delay 3 seconds
1464231208775: pool-1-thread-3: delay 3 seconds
1464231211775: pool-1-thread-3: delay 3 seconds
1464231214776: pool-1-thread-3: delay 3 seconds
1464231217777: pool-1-thread-3: delay 3 seconds
1464231220776: pool-1-thread-3: delay 3 seconds
2.4.newSingleThreadExecutor
創建一個單線程的線程池,以無界隊列方式運行。這個線程池只有一個線程在工作(如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。)此線程池能夠保證所有任務的執行順序按照任務的提交順序執行,同一時段只有一個任務在運行。
此類型線程池特別適合於需要保證執行順序的場合。
測試代碼:
/**
* 單線程池
*/
private static void testSingleThreadExecutor() {
System.out.println("這是單線程池:");
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
print(index + "");
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
輸出結果:
這是單線程池:
1464230560509: pool-1-thread-1: 0
1464230561514: pool-1-thread-1: 1
1464230562519: pool-1-thread-1: 2
1464230563524: pool-1-thread-1: 3
1464230564529: pool-1-thread-1: 4
1464230565534: pool-1-thread-1: 5
1464230566536: pool-1-thread-1: 6
1464230567541: pool-1-thread-1: 7
1464230568546: pool-1-thread-1: 8
1464230569551: pool-1-thread-1: 9
3.自定義線程池
3.1. 四種自帶線程池的實際構造方法:
1.CachedThreadPool:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
2.FixedThreadPool:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.ScheduledThreadPool:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
跳轉至:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
4.SingleThreadExecutor:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3.2.ThreadPoolExecutor構造方法:
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;
}
參數解釋:
- corePoolSize(線程池的基本大小):
當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閒的基本線程能夠執行新任務也會創建線程,等到需要執行的任務數大於線程池基本大小時就不再創建。如果調用了線程池的prestartAllCoreThreads方法,線程池會提前創建並啓動所有基本線程。- maximumPoolSize(線程池最大大小):
線程池允許創建的最大線程數。如果隊列滿了,並且已創建的線程數小於最大線程數,則線程池會再創建新的線程執行任務。值得注意的是如果使用了無界的任務隊列這個參數就沒什麼效果。- keepAliveTime(線程活動保持時間):
線程池的工作線程空閒後,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大這個時間,提高線程的利用率。- unit(線程活動保持時間的單位):
可選的單位有天(DAYS),小時(HOURS),分鐘(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。- workQueue(任務隊列):
用於保存等待執行的任務的阻塞隊列。 可以選擇以下幾個阻塞隊列。
- ArrayBlockingQueue:
是一個基於數組結構的有界阻塞隊列,此隊列按 FIFO(先進先出)原則對元素進行排序。- LinkedBlockingQueue:
一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。- SynchronousQueue:
一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列。- PriorityBlockingQueue:
一個具有優先級的無限阻塞隊列。- threadFactory(線程工廠):
用於設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置更有意義的名字。- handler(飽和策略):
當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採取一種策略處理提交的新任務。這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出異常。以下是JDK1.5提供的四種策略。
- AbortPolicy:直接拋出異常。
- CallerRunsPolicy:只用調用者所在線程來運行任務。
- DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務。
- DiscardPolicy:不處理,丟棄掉。當然也可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略。如記錄日誌或持久化不能處理的任務。
3.3.簡單的自定義線程池
核心有兩個線程,最大線程數量可無限,存活時間60s
測試代碼:
/**
* 自定義線程池
*/
private static void testCustomThreadPool() {
System.out.println("這是自定義線程池:");
ExecutorService customThreadExecutor = new ThreadPoolExecutor(
2, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
for (int i = 0; i < 10; i++) {
final int index = i;
customThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
print(index + "");
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
輸出結果:
這是自定義線程池:
1464235380782: pool-1-thread-1: 0
1464235380783: pool-1-thread-2: 1
1464235381787: pool-1-thread-2: 2
1464235381787: pool-1-thread-1: 3
1464235382790: pool-1-thread-2: 5
1464235382790: pool-1-thread-1: 4
1464235383793: pool-1-thread-1: 7
1464235383793: pool-1-thread-2: 6
1464235384798: pool-1-thread-1: 8
1464235384798: pool-1-thread-2: 9
參考目錄:
1. Java:多線程,線程池,用Executors靜態工廠生成常用線程池
2. Java(Android)線程池