為什么使用線程池
線程池作用就是通過限制系統(tǒng)中執(zhí)行線程的數(shù)量從而達到優(yōu)化資源分配的目的。
控制執(zhí)行線程的數(shù)量
假如現(xiàn)在有一個工作臺,上面只能有5個人作業(yè).
使用鎖
Java引入了
Semaphore
的一個類,稱為信號量
.可以指定開放多少把鎖
事例代碼
public class WorkPlace {
private static Semaphore semaphore = new Semaphore(5);
public static void main(String[] args) throws Exception {
/**
* 模擬10個工人去申請使用工作臺作業(yè)
*/
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
work();
};
}.start();
}
}
/*
* 工作臺的作業(yè)情景,每次只能有5個工人在上面作業(yè)
*/
public static void work() {
try {
semaphore.acquire(); //要工作了,獲取一把鎖
System.out.println(Thread.currentThread().getName() + "正在工作!");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "工作完畢!");
semaphore.release(); //工作完畢,釋放這把鎖
}
}
這樣就保證了每次最多有5個工人可以在上面作業(yè)
使用線程池實現(xiàn)
Java線程池類型
newSingleThreadExecutor
線程池中只有一個線程執(zhí)行,相當(dāng)于單線程.但是如果線程掛了之后(異常結(jié)束等),會重新啟動一個線程替原來的線程執(zhí)行下去
newFixedThreadPool
線程池的大小是固定的.每次提交一個任務(wù)就會創(chuàng)建一個線程,直到線程達到線程池的最大限制,里面有一個
任務(wù)隊列
去維護未執(zhí)行的任務(wù),當(dāng)有空閑的線程
的時候就會到任務(wù)隊列里面去取.
newCachedThreadPool
線程池中的線程是可緩存的.如果線程池的大小超過所需執(zhí)行任務(wù)的大小,則系統(tǒng)會回收
空閑線程
(60秒不執(zhí)行任務(wù)).當(dāng)任務(wù)數(shù)增加時,線程池又可以自動添加新線程來處理任務(wù).并且線程池不做大小的限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
- newScheduledThreadPool
線程池不限大小,并且支持任務(wù)
定時
與周期性循環(huán)
事例代碼
public class WorkPlace {
private static Executor executor = Executors.newFixedThreadPool(5);
// private static Executor executor = Executors.newScheduledThreadPool(5);//也可以
public static void main(String[] args) throws Exception {
/**
* 模擬100個工人去申請使用工作臺作業(yè)
*/
for (int i = 0; i < 100; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
work();
}
});
}
}
/*
* 工作臺的作業(yè)情景,每次只能有5個工人在上面作業(yè)
*/
public static void work() {
try {
System.out.println(Thread.currentThread().getName() + "正在工作!");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "工作完畢!");
}
}
簡單剖析Java的線程池類型
通過對方法的追蹤,我們可以看到上面的四種線程池都是來自同一個東西
ThreadPoolExecutor
那我們能不能嘗試使用ThreadPoolExecutor
這個類來實現(xiàn)線程池呢?下面繼續(xù)...
ThreadPoolExecutor構(gòu)造器參數(shù)
我們通過查看
ThreadPoolExecutor
類,可以看到有幾個主要的參數(shù).
-
corePoolSize
//核心線程數(shù)量 -
maximumPoolSize
//最大線程數(shù)量 -
keepAliveTime
//空閑線程存活時間 -
unit
//keepAliveTime的單位(時分秒等) -
workQueue
//任務(wù)隊列,用來存放將要執(zhí)行的任務(wù)(Runnable類型) -
threadFactory
//產(chǎn)生線程的方式 -
handler
//當(dāng)線程池不能接受任務(wù)時拋出的異常由該對象處理
錯綜復(fù)雜的corePoolSize maximumPoolSize workQueue關(guān)系
- 當(dāng)提交任務(wù)時當(dāng)前啟動的線程總數(shù)小于
corePoolSize
的時候,每次提交一個任務(wù),都會新建一個線程.- 當(dāng)提交任務(wù)時當(dāng)前啟動的線程總數(shù)大于等于
corePoolSize
的時候,就會把任務(wù)扔到workQueue
里面去,然后新建
或者復(fù)用
線程,并且保證啟動的線程總數(shù)小于等于maximumPoolSize
.- 每當(dāng)一個任務(wù)結(jié)束之后,線程池里面的線程就會從
workQueue
中取.
maximumPoolSize = 核心線程數(shù)量 + 非核心線程數(shù)量
也容易看出,提交的任務(wù)總數(shù)應(yīng)該小于maximumPoolSize + workQueue的大小
例如:maximumPoolSize = 10,workQueue的大小 = 20
當(dāng)提交31個任務(wù)的時候,所有線程10個全開.然后還有21個任務(wù)需要進入任務(wù)隊列里面等待被取走執(zhí)行,但是任務(wù)隊列的大小只有20,裝不下21.這時候就會拋出RejectedExecutionHandler
的異常
事例代碼
public class WorkPlace {
private static AtomicInteger count = new AtomicInteger(0);
private static LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(5);
private static Executor executor = new ThreadPoolExecutor(5, 5, 1, TimeUnit.SECONDS,
workQueue, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("線程" + count.getAndIncrement());
return thread;
}
});
public static void main(String[] args) throws Exception {
/**
* 模擬10個工人去申請使用工作臺作業(yè)
*/
for (int i = 0; i < 10; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
work();
}
});
}
}
/*
* 工作臺的作業(yè)情景,每次只能有5個工人在上面作業(yè)
*/
public static void work() {
try {
System.out.println(Thread.currentThread().getName() + "正在工作!");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "工作完畢!");
}
}
同樣,上面的代碼也能夠?qū)崿F(xiàn)前面的效果
當(dāng)我們把10個工人修改成11個工人的時候,程序就拋出RejectedExecutionHandler
異常了
AtomicInteger
是int
的另一個包裝類,不過是線程安全的.因為我們不清楚同時會有多少個對象去調(diào)用ThreadFactory
的newThread
方法,所以為了保證編號的正確性,所以使用AtomicInteger
代替int