簡介
為了給並發程序開發提供更好的支持,Java不僅提供了Thread類、Runnable接口等簡單的多線程支持工具,還提供了用於多線程管理的線程池,用於提高並發程序的性能。
無限制線程的缺陷
多線程的軟件設計方法確實可以提高多核處理器的計算能力,提高系統的性能和吞吐量,但是如果不加限制的使用多線程,對於系統性能不僅不能提升,反而會下降產生不利影響。
簡單的線程創建方法new Thread().start(),通過thread來啟動線程,並且由系統自動的回收線程。在簡單的系統這樣做並沒有問題,但是在真實的生產系統中,在某一時刻會有大量的請求,這樣就需要為每一個請求創建一個線程,而當線程數量過大時,會耗盡CPU和內存資源。
雖然線程是一種輕量級的工具,但是其創建和銷毀也需要消耗一定的時間;其次,線程本身也是需要占用內存空間的,大量的線程會搶占內存只有,導致Out of memory異常,並且大量線程的回收會給GC帶來很大壓力,延長GC停頓的時間。
因此,對線程的使用必須控制一個度,在適當的范圍內使用會提供系統性能,但是,一旦超過了這個范圍,大量的線程就會拖垮整個系統。在生產環境下,必須要對其進行管理和控制。
簡單線程池的實現
前面介紹在多線程中不斷的創建和銷毀線程會帶來額外的開銷,這樣就需要引入一種線程復用機制,即線程池。線程池的基本功能就是進行線程復用,當系統接受一個提交的任務,需要一個線程時,並不急於去創建一個線程,而是去現場池中尋找,是否有閒置的線程,若有,直接使用線程池中的線程工作,如沒有,再去創建新的線程。待任務完成後,也不是簡單的銷毀線程,而是將線程放回線程池中,以便下次復用。
上面已經把線程池實現的原理簡單說明了一下,下面我們自己實現一個線程,來了解一下線程池實現的核心功能,有助於理解線程池的實現。
線程池實現代碼:
public class ThreadPool {
private static ThreadPool instance = null;
//空閒線程隊列
private List<PThread> idelThreads;
//已有的線程總數
private int threadCounter;
private boolean isShutdown = false;
public ThreadPool() {
idelThreads = new Vector<PThread>(5);
threadCounter=0;
}
public synchronized int getCreatedThreadsCount() {
return threadCounter;
}
//取得線程池實例
public synchronized static ThreadPool getInstatce(){
if(instance==null){
instance = new ThreadPool();
}
return instance;
}
//把線程重新放回到池中
public synchronized void repool(PThread repoolThread){
if(!isShutdown){
idelThreads.add(repoolThread);
}else{
repoolThread.shutdown();
}
}
//停止池中所有線程
public synchronized void shutdown(){
isShutdown = true;
for (int i = 0; i < idelThreads.size(); i++) {
PThread pthread = idelThreads.get(i);
pthread.shutdown();
}
}
//執行任務
public synchronized void start(Runnable target){
PThread pthread = null;
//如果有閒置線程
if(idelThreads.size()>0){
int index = idelThreads.size()-1;
pthread=idelThreads.get(index);
idelThreads.remove(index);
pthread.setTarget(target);
}else{//如果沒有閒置線程
threadCounter++;
PThread p = new PThread(instance, target, "PThread#"+threadCounter);
p.start();
}
}
}
從代碼中可以看出,線程池中有一個閒置線程的隊列,在執行任務時,如果有閒置線程,則從線程池中取線程執行任務,如果沒有現在線程,則創建新的線程,並且在現場使用完畢後,會將線程重新放回到閒置線程隊列中。
另外,線程池的使用還需要一個永不退出的線程的配合使用,該線程在手動關閉前永不結束,並且一直等待新任務的到來。代碼如下:
public class PThread extends Thread{
//線程池
private ThreadPool pool;
//任務
private Runnable target;
private boolean isShutDown = false;
private boolean isIdle = false;
public PThread(ThreadPool pool, Runnable target,String name) {
super(name);
this.pool = pool;
this.target = target;
}
public synchronized Runnable getTarget() {
return target;
}
public synchronized boolean isIdle() {
return isIdle;
}
@Override
public void run() {
while(!isShutDown){
isIdle = false;
if(target!=null){
//運行任務
target.run();
}
//任務結束,閒置任務
isIdle=true;
try {
pool.repool(this);
synchronized (this) {
//線程閒置,等待任務到來
wait();
}
} catch (Exception e) {
// TODO: handle exception
}
isIdle=false;
}
}
public synchronized void setTarget(Runnable target){
this.target=target;
//設置任務之後,通知run方法,開始執行
notifyAll();
}
public synchronized void shutdown(){
isShutDown=true;
notifyAll();
}
}
執行線程:
public class MyThread implements Runnable{
private String name;
public MyThread() {
}
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
測試代碼:
public class TestClient {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
long begin = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
//new Thread(new MyThread("testnopoolThread"+i)).start();
ThreadPool.getInstatce().start(new MyThread("testpoolThread"+i));
//executor.execute(new MyThread("executorpoolThread"+i));
}
System.out.println(System.currentTimeMillis()-begin);
}
}
線程池能減少線程頻繁調度的開銷,線程的復用,對系統性能的提升效果比較明顯。
•Executor框架
JDK提供了一整套的Executor框架,幫助開發人員有效的進行線程控制。ThreadPoolExecutor表示一個線程池,Executors類扮演著線程池工廠的角色,通過Executor可以取得一個特定功能的線程池。
newFixedThreadPool():該方法返回一個固定線程數量的線程池,該線程池中線程的數量始終保持不變。當一個任務提交後,線程池中若有空閒線程則立即執行,若沒有,新任務會被保存在一個任務隊列中,待有現車空閒時,便處理任務隊列中的任務。
newSingleThreadExecutor():該方法返回只有一個線程的線程池。若多余的任務被提交到該線程池,任務會被保存到一個任務隊列中,若線程空閒,按先進先出的順序執行隊列中的任務。
newCacheThreadPool():該方法返回一個根據實際情況調整線程數量的線程池。若有空閒線程可以復用,則優先選擇使用可復用的線程,否則,創建新的線程處理新任務。
newSingleThreadScheduledExecutor()方法:該方法返回一個ScheduledExecutorService對象,線程池大小為1,在給定時間內執行某一任務。
•自定義線程池
newFixedThreadPool、newSingleThreadExecutor、newCacheThreadPool的內部實現都實現了ThreadPoolExecutor。在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;
}
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2016-03/129622p2.htm