天天看點

Java線程池

1.   線程池

1.1.  為什麼使用線程池

多線程的缺點:

處理任務的線程建立和銷毀都非常耗時并消耗資源。

多線程之間的切換也會非常耗時并消耗資源。

解決方法:采用線程池

使用時線程已存在,消除了線程建立的時耗

通過設定線程數目,防止資源不足

1.1.  ThreadPoolExecutor的全參構造函數參數介紹

在Java中建立線程池常用的類是ThreadPoolExecutor,該類的全參構造函數如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
      

  

參數介紹:

l  corePoolSize:線程池中核心線程數的最大值

l  maximumPoolSize:線程池中能擁有最多線程數

l  workQueue:用于緩存任務的阻塞隊列,對于不同的應用場景我們可能會采取不同的排隊政策,這就需要不同類型的阻塞隊列,線上程池中常用的阻塞隊列有以下2種:

  • SynchronousQueue<Runnable>:此隊列中不緩存任何一個任務。向線程池送出任務時,如果沒有空閑線程來運作任務,則入列操作會阻塞。當有線程來擷取任務時,出列操作會喚醒執行入列操作的線程。從這個特性來看,SynchronousQueue是一個無界隊列,是以當使用SynchronousQueue作為線程池的阻塞隊列時,參數maximumPoolSizes沒有任何作用。

LinkedBlockingQueue<Runnable>:顧名思義是用連結清單實作的隊列,可以是有界的,也可以是無界的,但在Executors中預設使用無界的。

以上三個參數之間的關系如下:

1)         如果沒有空閑的線程執行該任務且目前運作的線程數少于corePoolSize,則添加新的線程執行該任務。

2)         如果沒有空閑的線程執行該任務且目前的線程數等于corePoolSize同時阻塞隊列未滿,則将任務入隊列,而不添加新的線程。

3)         如果沒有空閑的線程執行該任務且阻塞隊列已滿同時池中的線程數小于maximumPoolSize,則建立新的線程執行任務。

4)         如果沒有空閑的線程執行該任務且阻塞隊列已滿同時池中的線程數等于maximumPoolSize,則根據構造函數中的handler指定的政策來拒絕新的任務。

l  keepAliveTime:表示空閑線程的存活時間。

unit:表示keepAliveTime的機關

l  handler:表示當workQueue已滿,且池中的線程數達到maximumPoolSize時,線程池拒絕添加新任務時采取的政策。一般可以采取以下四種取值。  

ThreadPoolExecutor.AbortPolicy() 抛出RejectedExecutionException異常
ThreadPoolExecutor.CallerRunsPolicy() 由向線程池送出任務的線程來執行該任務
ThreadPoolExecutor.DiscardOldestPolicy() 抛棄最舊的任務(最先送出而沒有得到執行的任務)
ThreadPoolExecutor.DiscardPolicy() 抛棄目前的任務

l  threadFactory:指定建立線程的工廠

1.1.  四種常用線程池

ThreadPoolExecutor構造函數的參數很多,使用起來很麻煩,JavaSE中又定義了Executors類,Eexcutors類提供了四個建立線程池的方法,分别如下

l  newCachedThreadPool

l  newFixedThreadPool

l  newSingleThreadExecutor

l  newScheduleThreadPool

1.1.1.    newCachedThreadPool

該方法建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。

此類型線程池特點是:

工作線程的建立數量幾乎沒有限制(其實也有限制的,數目為Interger.

1)         MAX_VALUE)

2)         空閑的工作線程會自動銷毀,有新任務會重新建立

在使用CachedThreadPool時,一定要注意控制任務的數量,否則,由于大量線程同時運作,很有會造成系統癱瘓

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceDemo {
	public static void main(String[] args) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			final int index = i;
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			cachedThreadPool.execute(new Runnable() {

				@Override
				public void run() {
					System.out.println(index);
				}
			});
		}
	}
}
      

1.1.1.    newFixedThreadPool

該方法建立一個指定工作線程數量的線程池。每當送出一個任務就建立一個工作線程,如果工作線程數量達到線程池初始的最大數,則将送出的任務存入到池隊列中。

優點:具有線程池提高程式效率和節省建立線程時所耗的開銷。

缺點:線上程池空閑時,即線程池中沒有可運作任務時,它不會釋放工作線程,還會占用一定的系統資源

public static void main(String[] args) {
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 {
						System.out.println(index);
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
}
      

  newSingleThreadExecutor

該方法建立一個單線程化的Executor,即隻建立唯一的工作者線程來執行任務,它隻會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO,優先級)執行。如果這個線程異常結束,會有另一個取代它,保證順序執行

單工作線程最大的特點是可保證順序地執行各個任務,并且在任意給定的時間不會有多個線程是活動的

public static void main(String[] args) {
		ExecutorService singleThreadExecutor =Executors.newSingleThreadExecutor();
		for(int i=0;i<10;i++){
			
			final int index=i;
			singleThreadExecutor.execute(new Runnable() {
				
				@Override
				public void run() {
					try {
						 System.out.println(index);  
	                        Thread.sleep(2000);  

					} catch (InterruptedException e) {
						e.printStackTrace(); 
					}
					
				}
			});
		}
	}
      

 newScheduleThreadPool

public class ScheduledExecutorServiceDemo {
	public static void main(String[] args) {
		ScheduledExecutorService scheduledThreadPool =Executors.newScheduledThreadPool(5);
		for(int i=0;i<10;i++){
			scheduledThreadPool.schedule(new Runnable() {
				
				@Override
				public void run() {
					System.out.println("delay 3 seconds");  
					
				}
			},3,TimeUnit.SECONDS);
		}
	}
}