您好,登錄后才能下訂單哦!
這篇文章主要介紹“線程池主要參數有哪些”,在日常操作中,相信很多人在線程池主要參數有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”線程池主要參數有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
線程(thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位,我們的程序最終都是由線程進行運作。在Java中,創建和銷毀線程的動作是很消耗資源的,因此就出現了所謂“池化資源”技術。
線程池是池化資源技術的一個應用,所謂線程池,顧名思義就是預先按某個規定創建若干個可執行線程放入一個容器中(線程池),需要使用的時候從線程池中去取,用完之后不銷毀而是放回去,從而減少了線程創建和銷毀的次數,達到節約資源的目的。
前面已經講到線程池的出現減少了線程創建和銷毀的次數,每個線程都可以被重復利用,可執行多個任務。
每當有任務到來時,直接復用線程池中的線程,而不需要等待新線程的創建,這個動作可以帶來響應速度的提升
可以根據系統的承受能力,調整線程池中的工作線程的數量,防止因為線程過多服務器變慢或死機。java一個線程默認占用空間為1M,可以想象一旦手動創建線程過多極有可能導致內存溢出。
我們可以用Executors類來創建一些常用的線程池,但是像阿里是禁止直接通過Executors類直接創建線程池的,具體的原因稍后再談。
在了解Executors類所提供的幾個線程池前,我們首先來了解一下 ThreadPoolExecutor的主要參數,ThreadPoolExecutor是創建線程池的類,我們選取參數最多的構造方法來看一下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
名稱 | 類型 | 含義 |
---|---|---|
corePoolSize | int | 核心線程池的大小 |
maximumPoolSize | int | 最大線程池大小 |
keepAliveTime | long | 線程最大空閑時間 |
unit | TimeUnit | 時間單位 |
workQueue | BlockingQueue<Runnable> | 線程等待隊列 |
threadFactory | ThreadFactory | 線程創建工程 |
handler | RejectedExecutionHandler | 拒絕策略 |
當向線程池提交一個任務時,如果線程池中已創建的線程數量小于corePoolSIze,即便存在空閑線程,也會創建一個新線程來執行任務,直到創建的線程數大于或等于corePoolSIze。
線程池所允許的最大線程個數,當隊列滿了且已經創建的線程數小于maximumPoolSize時,會創建新的線程執行任務。
當線程中的線程數大于corePoolSIze時,如果線程空閑時間大于keepAliveTime,該線程就會被銷毀。
keepAliveTime的時間單位
用于保存等待執行任務的隊列
用于創建新線程。threadFactory創建的線程也是采用new Thread()方式,threadFactory創建的線程名都具有統一的風格:pool-m-thread-n
拒絕策略,當線程池和隊列滿了之后,再加入新線程后會執行此策略。 下面是四種線程池的拒絕策略:
AbortPolicy:中斷任務并拋出異常
DiscardPolicy:中段任務但是不拋出異常
DiscardOldestPolicy:丟棄隊列中最老的任務,然后嘗試提交新任務
CallerRunsPolicy:由調用線程處理該任務
當我們了解了ThreadPoolExecutor的七個參數后,我們就可以很快的理解線程池的流程:
當提交任務后,首先判斷當前線程數是否超過核心線程數,如果沒超過則創建新線程執行任務,否則判斷工作隊列是否已滿,如果未滿則將任務添加到隊列中,否則判斷線程數是否超過最大線程數,如果未超過則創建線程執行任務,否則執行拒絕策略。
executors提供了許多種線程池供用戶使用,雖然很多公司禁止使用executors創建線程池,但是對于剛開始解除線程池的人來說,Executors類所提供的線程池能很好的帶你進入多線程的世界。
ExecutorService executorService = Executors.newSingleThreadExecutor();
聽名字就可以知道這是一個單線程的線程池,在這個線程池中只有一個線程在工作,相當于單線程執行所有任務,此線程可以保證所有任務的執行順序按照提交順序執行,看構造方法也可以看出,corePoolSize和maximumPoolSize都是1。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
ExecutorService executorService = Executors.newFixedThreadPool(2);
固定長度的線程池,線程池的長度在創建時通過變量傳入。下面是newFixedThreadPool的構造方法,corePoolSize和maximumPoolSize都是傳入的參數值
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
ExecutorService executorService = Executors.newCachedThreadPool();
可緩存線程池,這個線程池設定keepAliveTime為60秒,并且對最大線程數量幾乎不做控制。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
觀察構造方法,corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即線程數量幾乎無限制。設定keepAliveTime 為60秒,線程空閑60秒后自動結束,因為該線程池創建無限制,不會有隊列等待,所以使用SynchronousQueue同步隊列。
創建一個定時的線程池。此線程池支持定時以及周期性執行任務的需求。下面是newScheduledThreadPool的用法:
Thread thread1=new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"thread1"); } }); Thread thread2=new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"thread2"); } }); Thread thread3=new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"thread3"); } }); ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); //在1000ms后執行thread1 scheduledExecutorService.schedule(thread1,1000,TimeUnit.MILLISECONDS); //在1000ms后每隔1000ms執行一次thread2,如果任務執行時間比間隔時間長,則延遲執行 scheduledExecutorService.scheduleAtFixedRate(thread2,1000,1000,TimeUnit.MILLISECONDS); //和第二種方式類似,但下一次任務開始的時間為:上一次任務結束時間(而不是開始時間) + delay時間 scheduledExecutorService.scheduleWithFixedDelay(thread3,1000,1000,TimeUnit.MILLISECONDS);
如果你的idea裝了Alibaba Java Codeing Guidelines插件(推薦大家使用,有助于讓你的代碼更加規范),那么當你寫了Exectors創建線程池后會看到提示:
并且阿里將這個用法定義為Blocker,即不允許使用,而是讓人們用ThreadPoolExecutor的方式創建線程池。原因是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的人更加明確線程池的運行規則,規避資源耗盡的風險。
Executors返回的線程池對象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
??允許的請求隊列長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。 ?? 2)CachedThreadPool:
??允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。 ?? 下面是ThreadPoolExecutor創建線程池的簡單例子
int corePoolSize=5; int maximumPoolSize=10; long keepAliveTime=30; BlockingQueue<Runnable> blockingQueue=new ArrayBlockingQueue(2); RejectedExecutionHandler handler=new ThreadPoolExecutor.AbortPolicy(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, blockingQueue, handler); threadPoolExecutor.execute(thread1);
SpringBoot對線程池又做了一層封裝,在SpringBoot中,可以通過ThreadPoolTaskExecutor類來創建線程池。需要注意兩者的區別ThreadPoolExecutor時JUC包下的類,ThreadPoolTaskExecutor是springframework包下的類。但原理都是一樣的。
首先搭建一個SpringBoot項目,只需要引入web依賴即可
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
線程池的參數盡量寫到配置文件里,這樣就能根據需要進行修改,在application.properties文件中配置:
myExecutor.corePoolSize=5 myExecutor.maxPoolSize=10 myExecutor.keepAliveSeconds=30 myExecutor.allowCoreThreadTimeOut=false myExecutor.queueCapacity=20 myExecutor.threadNamePrefix=myExecutor-
新建一個包叫config,新建類ExecutorConfig ,首先參數的值從配置文件中獲取,接著用ThreadPoolTaskExecutor 創建一個自己的線程池,注入到Bean容器中。
@Configuration @EnableAsync public class ExecutorConfig { @Value("${myExecutor.corePoolSize}") private int corePoolSize; @Value("${myExecutor.maxPoolSize}") private int maxPoolSize; @Value("${myExecutor.keepAliveSeconds}") private int keepAliveSeconds; @Value("${myExecutor.allowCoreThreadTimeOut}") private boolean allowCoreThreadTimeOut; @Value("${myExecutor.queueCapacity}") private int queueCapacity; @Value("${myExecutor.threadNamePrefix}") private String threadNamePrefix; @Bean("myExecutor") public Executor myExecutor(){ ThreadPoolTaskExecutor executor=new ThreadPoolTaskExecutor(); //核心線程數 executor.setCorePoolSize(corePoolSize); //最大線程數 executor.setMaxPoolSize(maxPoolSize); //線程空閑時間 executor.setKeepAliveSeconds(keepAliveSeconds); //是否保留核心線程數 executor.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut); //隊列長度 executor.setQueueCapacity(queueCapacity); //拒絕策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); //設置線程名稱前綴 executor.setThreadNamePrefix(threadNamePrefix); executor.initialize(); return executor; } }
這里需要注意的是有一個方法setAllowCoreThreadTimeOut,當傳入參數為true時,所有線程超時后都會被銷毀,如果為false,只有超過核心線程數并且超時時才會被銷毀。
首先寫一個service接口:
public interface DoSomeThing { /** * 通過線程池異步執行耗時的任務 */ public void doSomeThing(); }
再編寫實現類:
@Service public class DoSomeThingImpl implements DoSomeThing { @Override @Async("myExecutor") public void doSomeThing() { System.out.println(Thread.currentThread().getName()+"-in"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"-out"); } }
增加注解@Async("myExecutor")后,這段方法就會異步由myExecutor線程池執行。
新建一個IndexController,每次請求執行一次doSomeThing()方法。
@RestController public class IndexController { @Autowired private DoSomeThing doSomeThing; @RequestMapping(value = "/index",method = RequestMethod.GET) public String index(){ doSomeThing.doSomeThing(); return "success"; } }
訪問十次http://localhost:8080/index,由于設置的核心線程數是5,隊列容量是30,因此最多只會用到5個線程資源,結果如下:
到此,關于“線程池主要參數有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。