系統啓動一個新線程的成本是比較高的,由於它涉及到與操做系統的交互。在這種狀況下,使用線程池能夠很好的提供性能,尤爲是當程序中須要建立大量生存期很短暫的線程時,更應該考慮使用線程池。html
與數據庫鏈接池相似的是,線程池在系統啓動時即建立大量空閒的線程,程序將一個Runnable對象傳給線程池,線程池就會啓動一條線程來執行該對象的run方法,當run方法執行結束後,該線程並不會死亡,而是再次返回線程池中成爲空閒狀態,等待執行下一個Runnable對象的run方法。
java
除此以外,使用線程池能夠有效地控制系統中併發線程的數量,但系統中包含大量併發線程時,會致使系統性能劇烈降低,甚至致使JVM崩潰。而線程池的最大線程數參數能夠控制系統中併發的線程不超過此數目。數據庫
在JDK1.5以前,開發者必須手動的實現本身的線程池,從JDK1.5以後,Java內建支持線程池。緩存
與多線程併發的全部支持的類都在java.lang.concurrent包中。咱們可使用裏面的類更加的控制多線程的執行。多線程
1、Executors類併發
JDK1.5中提供Executors工廠類來產生鏈接池,該工廠類中包含以下的幾個靜態工程方法來建立鏈接池:app
一、public static ExecutorService newFixedThreadPool(int nThreads):建立一個可重用的、具備固定線程數的線程池。ide
二、public static ExecutorService newSingleThreadExecutor():建立一個只有單線程的線程池,它至關於newFixedThreadPool方法是傳入的參數爲1性能
三、public static ExecutorService newCachedThreadPool():建立一個具備緩存功能的線程池,系統根據須要建立線程,這些線程將會被緩存在線程池中。spa
四、public static ScheduledExecutorService newSingleThreadScheduledExecutor:建立只有一條線程的線程池,他能夠在指定延遲後執行線程任務
五、public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):建立具備指定線程數的線程池,它能夠再指定延遲後執行線程任務,corePoolSize指池中所保存的線程數,即便線程是空閒的也被保存在線程池內。
上面的幾個方法都有一個重載的方法,多傳入一個ThreadFactory參數的重載方法,使用的比較少。
2、ExecutorService類
能夠看到上面的5個方法中,前面3個方法的返回值都是一個ExecutorService對象。該ExecutorService對象就表明着一個儘快執行線程的線程池(只要線程池中有空閒線程當即執行線程任務),程序只要將一個Runnable對象或Callable對象提交給該線程池便可,該線程就會盡快的執行該任務。
ExecutorService有幾個重要的方法:
方法摘要 | ||
---|---|---|
boolean |
isShutdown() 若是此執行程序已關閉,則返回 true。 |
|
boolean |
isTerminated() 若是關閉後全部任務都已完成,則返回 true。 |
|
void |
shutdown() 啓動一次順序關閉,執行之前提交的任務,但不接受新任務。 |
|
List<Runnable> |
shutdownNow() 試圖中止全部正在執行的活動任務,暫停處理正在等待的任務,並返回等待執行的任務列表。 |
|
|
submit(Callable<T> task) 提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future。 |
|
Future<?> |
submit(Runnable task) 提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。 |
|
|
submit(Runnable task, T result) 提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。 |
更詳細的參考JDK API文檔。
submit方法是對 Executor接口execute方法的更好的封裝,建議使用submit方法。
3、ScheduleExecutorService類
在上面的5個方法中,後面2個方法的返回值都是一個ScheduleExecutorService對象。ScheduleExecutorService表明可在指定延遲或週期性執行線程任務的線程池。
ScheduleExecutorService類是ExecutorService類的子類。因此,它裏面也有直接提交任務的submit方法,而且新增了一些延遲任務處理的方法:
方法摘要 | ||
---|---|---|
|
schedule(Callable<V> callable, long delay, TimeUnit unit) 建立並執行在給定延遲後啓用的 ScheduledFuture。 |
|
ScheduledFuture<?> |
schedule(Runnable command, long delay, TimeUnit unit) 建立並執行在給定延遲後啓用的一次性操做。 |
|
ScheduledFuture<?> |
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 建立並執行一個在給定初始延遲後首次啓用的按期操做,後續操做具備給定的週期;也就是將在 initialDelay後開始執行,而後在 initialDelay+period 後執行,接着在 initialDelay + 2 * period 後執行,依此類推。 |
|
ScheduledFuture<?> |
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 建立並執行一個在給定初始延遲後首次啓用的按期操做,隨後,在每一次執行終止和下一次執行開始之間都存在給定的延遲。 |
下面看看線程池的簡單使用:
一、固定大小的線程池:
[java] view plaincopy
package com.tao.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newFixedThreadPool(5);//建立一個固定大小爲5的線程池
for(int i=0;i<7;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在執行。。。");
}
}
輸出結果:
[java] view plaincopy
pool-1-thread-1正在執行。。。
pool-1-thread-3正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-4正在執行。。。
pool-1-thread-4正在執行。。。
pool-1-thread-5正在執行。。。
pool-1-thread-1正在執行。。。
能夠看到雖然咱們呢建立了7個MyThread線程對象,可是因爲受線程池的大小限制,只是開啓了5個線程,這樣就減小了併發線程的數量。
二、單任務線程池:
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newSingleThreadExecutor();//建立一個單線程池
for(int i=0;i<7;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
輸出結果:
[java] view plaincopy
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
能夠看到,線程池只開啓了一個線程。
三、建立可變尺寸的線程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ExecutorService pool=Executors.newCachedThreadPool();
for(int i=0;i<5;i++){
pool.submit(new MyThread());
}
pool.shutdown();
}
}
看輸出結果:
[java] view plaincopy
pool-1-thread-1正在執行。。。
pool-1-thread-3正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-4正在執行。。。
pool-1-thread-5正在執行。。。
能夠看到,咱們沒有限制線程池的大小,可是它會根據需求而建立線程。
四、延遲線程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ScheduledExecutorService pool=Executors.newScheduledThreadPool(6);
for(int i=0;i<4;i++){
pool.submit(new MyThread());
}
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.shutdown();
}
}
輸出結果:
[java] view plaincopy
pool-1-thread-1正在執行。。。
pool-1-thread-3正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-4正在執行。。。
pool-1-thread-6正在執行。。。
pool-1-thread-1正在執行。。。
能夠明顯看到,最後兩個線程不是當即執行,而是延遲了1秒在執行的。
五、單任務延遲線程池
[java] view plaincopy
public class PoolTest {
public static void main(String[] args) {
ScheduledExecutorService pool=Executors.newSingleThreadScheduledExecutor();
for(int i=0;i<4;i++){
pool.submit(new MyThread());
}
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.schedule(new MyThread(), 1000, TimeUnit.MILLISECONDS);
pool.shutdown();
}
}
上面咱們使用的是JDK幫我封裝好的線程池,咱們也能夠本身定義線程池,查看源碼,咱們發現,Excutors裏面的得到線程的靜態方法,內部都是調用ThreadPoolExecutor的構造方法。好比:
[java] view plaincopy
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
能夠看到,它是經過調用ThreadPoolExecutor的構造方法來返回一個線程池的。因此,咱們也能夠本身手動的調用ThreadPoolExecutor的各類構造方法,來定義本身的線程池規則,不過通常狀況下,使用自帶的線程池就夠了,不須要本身來實現。