線程池
1. 執行器
????????In all of the previous examples, there's a close connection between the task being done by a new thread, as defined by its Runnable object, and the thread itself, as defined by a Thread object. This works well for small applications, but in large-scale applications, it makes sense to separate thread management and creation from the rest of the application. Objects that encapsulate these functions are known as executors.????????在前面的所有示例中,由新線程(由其Runnable 對象定義)執行的任務與由 Thread 對象定義的線程本身之間存在緊密的聯系。 這對于小型應用程序非常有效,但是在大型應用程序中,將線程管理和創建與其余應用程序分開是有意義的。 封裝這些功能的對象稱為執行器。
Executor接口方法
void execute ( Runnable command ); // 將任務添加到線程池中,等待線程池調度執行
ExecutorService接口常用方法
void shutdown (); // 有序關閉線程池,不再接收新的線程任務,但池中已有任務會執行List < Runnable > shutdownNow (); // 關閉線程池,嘗試停止所有正在執行的任務,并將池中等待執行的任務返回boolean isShutdown (); // 檢測線程池是否已經關閉boolean isTerminated (); // 檢測線程池是否已經終止Future <?> submit ( Runnable task ); // 提交一個任務至線程池中
2.線程池
????????Most of the executor implementations in java.util.concurrent use thread pools, which consist of worker threads. This kind of thread exists separately from the Runnable and Callable tasks it executes and is often used to execute multiple tasks.????????java.util.concurrent中的大多數執行程序實現都使用線程池,該線程池由工作線程組成。這種線程與它執行的Runnable 和 Callable 任務分開存在,通常用于執行多個任務。????????Using worker threads minimizes the overhead due to thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and deallocating many thread objects creates a significant memory management overhead.????????使用工作線程可以最大程度地減少線程創建所帶來的開銷。線程對象占用大量內存,在大型應用程序中,分配和取消分配許多線程對象會產生大量內存管理開銷。????????One common type of thread pool is the fixed thread pool. This type of pool always has a specified number of threads running; if a thread is somehow terminated while it is still in use, it is automatically replaced with a new thread. Tasks are submitted to the pool via an internal queue, which holds extra tasks whenever there are more active tasks than threads.????????線程池的一種常見類型是固定線程池。這種類型的池始終具有指定數量的正在運行的線程。如果某個線程在仍在使用時以某種方式終止,則它將自動替換為新線程。任務通過內部隊列提交到池中,該內部隊列在活動任務多于線程時容納額外的任務。????????An important advantage of the fixed thread pool is that applications using it degrade gracefully.????????固定線程池的一個重要優點是使用該線程池的應用程序可以正常降級
線程池構造方法
public ThreadPoolExecutor ( int corePoolSize , // 核心線程數int maximumPoolSize , // 最大線程數long keepAliveTime , // 工作線程存活時間TimeUnit unit , // 時間單位BlockingQueue < Runnable > workQueue , // 任務隊列ThreadFactory threadFactory , // 線程工廠RejectedExecutionHandler handler ) // 拒絕處理器
示例
import java.util.Queue; import java.util.concurrent.*; public class ThreadPoolTest {public static void main(String[] args) {LinkedBlockingDeque<Runnable> taskQueue = new LinkedBlockingDeque<>(10);ThreadPoolExecutor pool = new ThreadPoolExecutor(5, //核心線程數10, //最大工作線程數2,//非核心線程的工作線程存活時間TimeUnit.SECONDS,//存活時間單位taskQueue,//任務隊列Executors.defaultThreadFactory(),//線程池中的線程創建工廠new ThreadPoolExecutor.AbortPolicy());//拒絕新線程任務的策略for(int i=0; i<30; i++){pool.submit(new ThreadPoolTask(i));int corePoolSize = pool.getCorePoolSize();//獲取核心線程數int size = pool.getQueue().size(); //獲取隊列中任務個數long finish = pool.getCompletedTaskCount();//獲取線程池執行完成任務的個數System.out.printf("線程池中核心線程數:%d,隊列中任務個數:%d,線程池完成任務數:%d\n",corePoolSize, size, finish);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}pool.shutdown();//關閉線程池,等待線程池中的任務執行完成,但是不會接收新的線程任務}static class ThreadPoolTask implements Runnable{private int num;public ThreadPoolTask(int num) {this.num = num;}@Overridepublic void run() {System.out.println("正在執行線程任務" + num);try {Thread.sleep(400);} catch (InterruptedException e) {e.printStackTrace();}}} }
線程池工作流程
????????線程池啟動后,核心線程就已經啟動,當一個新的任務提交到線程池時,首先會檢測當前是否存在空閑的核心線程,如果存在,就將該任務交給這個空閑核心線程執行。如果不存在,那么就將該任務交給隊列,在隊列中排隊等候。如果隊列滿了,此時線程池會檢測當前工作線程數是否達到最大線程數,如果沒有達到最大線程數,那么將由線程工廠創建新的工作線程來執行隊列中的任務,這樣,隊列中就有空間能夠容納這個新任務。如果創建的工作線程在執行完任務后,在給定的時間范圍內沒有新的任務執行,這些工作線程將死亡。如果已經達到最大線程數,那么線程池將采用提供的拒絕處理策略來拒絕這個新任務。
線程池創建方式
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorTest {public static void main(String[] args) { //創建一個給定核心線程數以及最大線程數的線程池,該線程池隊列非常大ExecutorService pool1 = Executors.newFixedThreadPool(5); //創建只有一個核心線程數以及最大線程數的線程池,該線程池隊列非常大ExecutorService pool2 = Executors.newSingleThreadExecutor(); //創建一個核心線程為0,最大線程數為整數的最大值的可緩存的線程池ExecutorService pool3 = Executors.newCachedThreadPool(); //創建一個給定核心線程數,最大線程數為整數的最大值的可調度的線程池ExecutorService pool4 = Executors.newScheduledThreadPool(5);} }
3. 線程池的使用
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecutorTaskTest {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(5);for (int i = 0; i < 100; i++) {int order = i;service.submit(() -> System.out.println("正在執行任務" + order));}service.shutdown();} }