boost创建线程池_Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池
目錄
- 背景
- 線程池的來由
- 什么是線程池
- 背景總結(jié)
- 用法
- 通過Executors創(chuàng)建線程池
- Executors及其服務(wù)的類
- Executors常用的幾個(gè)方法
- 一個(gè)線程池的例子
- 任務(wù)
- 池子
- 測試
- 說明
- 通過Executors創(chuàng)建線程池
- 總結(jié)
- Links
- 文章友鏈
- 相關(guān)資源
背景
線程池的來由
服務(wù)端的程序,例如數(shù)據(jù)庫服務(wù)器和Web服務(wù)器,每次收到客戶端的請求,都會創(chuàng)建一個(gè)線程來處理這些請求。
創(chuàng)建線程的方式又很多,例如繼承Thread類、實(shí)現(xiàn)Runnable或者Callable接口等。
通過創(chuàng)建新的線程來處理客戶端的請求,這種看起來很容易的方法,其實(shí)是有很大弊端且有很高的風(fēng)險(xiǎn)的。
俗話說,簡單的路越走越困難,困難的路越走越簡單,就是這個(gè)道理。
創(chuàng)建和銷毀線程,會消耗大量的服務(wù)器資源,甚至創(chuàng)建和銷毀線程消耗的時(shí)間比線程本身處理任務(wù)的時(shí)間還要長。
由于啟動線程需要消耗大量的服務(wù)器資源,如果創(chuàng)建過多的線程會造成系統(tǒng)內(nèi)存不足(run out of memory),因此限制線程創(chuàng)建的數(shù)量十分必要。
什么是線程池
線程池通俗來講就是一個(gè)取出和放回提前創(chuàng)建好的線程的池子,概念上,類似數(shù)據(jù)庫的連接池。
那么線程池是如何發(fā)揮作用的呢?
實(shí)際上,線程池是通過重用之前創(chuàng)建好線程來處理當(dāng)前任務(wù),來達(dá)到大大降低線程頻繁創(chuàng)建和銷毀導(dǎo)致的資源消耗的目的。
A thread pool reuses previously created threads to execute current tasks and offers a solution to the problem of thread cycle overhead and resource thrashing. Since the thread is already existing when the request arrives, the delay introduced by thread creation is eliminated, making the application more responsive.背景總結(jié)
下面總結(jié)一下開篇對于線程池的一些介紹。
但到底怎么使用線程池呢?線程池真的這么簡單好用嗎?線程池使用的過程中有沒有什么坑?
不要著急,下面就結(jié)合具體的示例,跟你講解各種使用線程池的姿勢,以及這些姿勢爽在哪里,痛在哪里。
準(zhǔn)備好紙巾,咳咳...,是筆記本,濤哥要跟你開講啦!
用法
通過Executors創(chuàng)建線程池
Executors及其服務(wù)的類
java.util.concurrent.Executors是JDK的并發(fā)包下提供的一個(gè)工廠類(Factory)和工具類(Utility)。
Executors提供了關(guān)于Executor, ExecutorService, ScheduledExecutorService, ThreadFactory 和 Callable相關(guān)的工廠方法和工具方法。
Executor是一個(gè)執(zhí)行提交的Runnable Tasks的對象,它有一個(gè)execute方法,參數(shù)是Runnable。當(dāng)執(zhí)行execute方法以后,會在未來某個(gè)時(shí)間,通過創(chuàng)建線程或者使用線程池中的線程的方式執(zhí)行參數(shù)中的任務(wù)。用法如下:
Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2());ExecutorService繼承了Executor,并提供了更多有意思的方法,比如shutdown方法會讓ExecutorService拒絕創(chuàng)建新的線程來執(zhí)行task。
Executors常用的幾個(gè)方法
//創(chuàng)建固定線程數(shù)量的線程池 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);//創(chuàng)建一個(gè)線程池,該線程池會根據(jù)需要創(chuàng)建新的線程,但如果之前創(chuàng)建的線程可以使用,會重用之前創(chuàng)建的線程 ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//創(chuàng)建一個(gè)只有一個(gè)線程的線程池 ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();一個(gè)線程池的例子
下面我就創(chuàng)建5個(gè)Task,并通過一個(gè)包含3個(gè)線程的線程池來執(zhí)行任務(wù)。我們一起看下會發(fā)生什么。
Github 完整代碼: 一個(gè)線程池的例子
?
ThreadPoolExample1就是我們的測試類,下面所有的內(nèi)部類、常量和方法都寫在這個(gè)測試類里。
package net.ijiangtao.tech.concurrent.jsd.threadpool;import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ThreadPoolExample1 {}任務(wù)
Task內(nèi)部類執(zhí)行了兩次for循環(huán),并在每次循環(huán)執(zhí)行結(jié)束以后 sleep 1秒鐘。
// Task class to be executed (Step 1) static class Task implements Runnable {private String name;public Task(String s) {name = s;}// Prints task name and sleeps for 1s// This Whole process is repeated 2 timespublic void run() {try {for (int i = 0; i <= 1; i++) {if (i == 0) {//prints the initialization time for every taskprintTimeMsg("Initialization");} else {// prints the execution time for every taskprintTimeMsg("Executing");}Thread.sleep(1000);}System.out.println(name + " complete");} catch (InterruptedException e) {e.printStackTrace();}}private void printTimeMsg(String state) {Date d = new Date();SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");System.out.println(state+" Time for"+ " task name - " + name + " = " + ft.format(d));} }池子
創(chuàng)建一個(gè)固定線程數(shù)的線程池。
// Maximum number of threads in thread pool static final int MAX_T = 3; // creates a thread pool with MAX_T no. of // threads as the fixed pool size(Step 2) private static final ExecutorService pool = Executors.newFixedThreadPool(MAX_T);測試
創(chuàng)建5個(gè)任務(wù),并通過線程池的線程執(zhí)行這些任務(wù)。
public static void main(String[] args) {// creates five tasksRunnable r1 = new Task("task 1");Runnable r2 = new Task("task 2");Runnable r3 = new Task("task 3");Runnable r4 = new Task("task 4");Runnable r5 = new Task("task 5");// passes the Task objects to the pool to execute (Step 3)pool.execute(r1);pool.execute(r2);pool.execute(r3);pool.execute(r4);pool.execute(r5);// pool shutdown ( Step 4)pool.shutdown(); }執(zhí)行結(jié)果如下。
Initialization Time for task name - task 1 = 12:39:44 Initialization Time for task name - task 2 = 12:39:44 Initialization Time for task name - task 3 = 12:39:44 Executing Time for task name - task 3 = 12:39:45 Executing Time for task name - task 1 = 12:39:45 Executing Time for task name - task 2 = 12:39:45 task 2 complete Initialization Time for task name - task 4 = 12:39:46 task 3 complete Initialization Time for task name - task 5 = 12:39:46 task 1 complete Executing Time for task name - task 5 = 12:39:47 Executing Time for task name - task 4 = 12:39:47 task 5 complete task 4 complete說明
從輸出的結(jié)果我們可以看到,5個(gè)任務(wù)在包含3個(gè)線程的線程池執(zhí)行。
由于線程的執(zhí)行有一定的隨機(jī)性,以及不同機(jī)器的資源情況不同,每次的執(zhí)行結(jié)果,可能會有差異。
下面是我第二次執(zhí)行的結(jié)果。
Initialization Time for task name - task 1 = 12:46:33 Initialization Time for task name - task 3 = 12:46:33 Initialization Time for task name - task 2 = 12:46:33 Executing Time for task name - task 2 = 12:46:34 Executing Time for task name - task 3 = 12:46:34 Executing Time for task name - task 1 = 12:46:34 task 3 complete task 2 complete task 1 complete Initialization Time for task name - task 4 = 12:46:35 Initialization Time for task name - task 5 = 12:46:35 Executing Time for task name - task 4 = 12:46:36 Executing Time for task name - task 5 = 12:46:36 task 5 complete task 4 completetask 1 2 3 獲得線程資源,task 4 5排隊(duì)等待:
task 1 2 3 執(zhí)行結(jié)束,task 4 5獲得線程資源,線程池中有一個(gè)線程處于空閑狀態(tài):
但規(guī)律是相同的,那就是線程池會將自己的線程資源貢獻(xiàn)出來,如果任務(wù)數(shù)超出了線程池的線程數(shù),就會阻塞并排隊(duì)等待有可用的線程資源以后執(zhí)行。
也就是線程池會保證你的task在將來(Future)的某個(gè)時(shí)間執(zhí)行,但并不能保證什么時(shí)間會執(zhí)行。
相信你現(xiàn)在對于ExecutorService的invokeAll方法,可以執(zhí)行一批task并返回一個(gè)Future集合,就會有更深入的理解了。
List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException通過ExecutorService線程池執(zhí)行task的過程如下圖所示,超出線程池線程數(shù)量的task將會在BlockingQueue排隊(duì)等待獲得線程資源的機(jī)會。
關(guān)于并發(fā)編程中的Futrue,筆者有一篇文章(Java并發(fā)編程-Future系列之Future的介紹和基本用法)專門介紹,請通過下面任意的鏈接移步欣賞:
- Future-掘金
- Future-簡書
總結(jié)
本教程帶領(lǐng)大家了解了線程池的來由、概念和基本用法,相信大家看完,以后就不再只會傻傻地new Thread了。
本節(jié)只是線程池的入門,下面會介紹關(guān)于線程池的更多武功秘籍,希望大家持續(xù)關(guān)注,有所獲益。
喜歡請點(diǎn)贊轉(zhuǎn)發(fā),如果大家對這個(gè)系列感興趣,我會繼續(xù)更新的。
Links
文章友鏈
- Java并發(fā) 之 Future系列 (1) Future的介紹和基本用法
- Github-JavaStudyDemo-Concurrent
相關(guān)資源
Concurrent-ThreadPool-線程池拒絕策略RejectedExecutionHandler
Concurrent-ThreadPool-ThreadPoolExecutor里面4種拒絕策略
Concurrent-ThreadPool-線程池ThreadPoolExecutor構(gòu)造方法和規(guī)則
Concurrent-ThreadPool-線程池的成長之路
Concurrent-ThreadPool-LinkedBlockingQueue和ArrayBlockingQueue的異同
Concurrent-ThreadPool-最佳線程數(shù)總結(jié)
Concurrent-ThreadPool-最佳線程數(shù)
Concurrent-ThreadPool-Thread Pools in Java
Concurrent-ThreadPool-java-thread-pool
Concurrent-ThreadPool-thread-pool-java-and-guava
Concurrent-ThreadPool-ijiangtao.net
總結(jié)
以上是生活随笔為你收集整理的boost创建线程池_Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 姑娘对象是军官结婚政审但是我18就没在老
- 下一篇: r语言绘制精美pcoa图_R语言绘制交互