線程池在代碼中用的還是比較多,不管什麼,都是用的池子,比如JDBC。在做一個生活中的例子吧,比如說你洗菜肯定會給要洗的才放入洗菜盆中來洗(洗菜盆就相當於一個池子),你肯定不會一根一根的洗,浪費水資源不說,最主要的還浪費水資源。
請原諒我是一個平頂山人,我對象都說我很囉嗦,但是我也沒有感覺。
a).什麼是線程池以及它的作用
b).JAVA開發中,合理的使用線程池有三大好處;
c).線程池的分類
e).線程池的原理分析
f).合理的配置線程數數量
------------------------------------------------------華麗的分界線------------------------------------------------------
a).什麼是線程池以及它的作用
JAVA中線程池是運用最多的併發框架,幾乎所有併發或者異步的編程都可以使用線程池。
主要就是通過幾個固定的線程來操作服務,減少了創建和銷燬線程的過程,從而提高效率。
b).JAVA開發中,合理的使用線程池有三大好處;
a).降低消耗資源
通過重複的創建線程以及銷燬線程能夠降低消耗
b).提高響應速度
當 達到任務時候,任務可以不等到線程創建就能執行任務
c).提高線程的可管理性
線程比較稀缺,如果過度的創建線程,不僅不能夠優化代碼,而且還會降低系統的性能。可以使用I線程池統一分配,管理,監控。
c).線程池的分類
前言:JAVA支持併發,但是頻繁的創建線程和銷燬線程會很浪費資源的,並且線程又很寶貴的。在JDK1.5以前的版本中,線程池非常的醜陋的,但是在JDK1.5之後加入了java.uril.concurrent包,對併發有了一個很大的幫助。今天主要說下Executor接口。
d).線程池創建的四種方式
前言:Executor框架最頂層的是ThreadPoolExecutor類,Executors主要提供的有newScheduledThreadPool,newFixedThreadPool,newCachedThreadPool,newSingleThreadExcutor,只是構造方法,參數不同,可以適用不同的場景
第一種:newCachedThreadPool:
創建一個可緩存的線程池,緩存線程池如果超過處理需要,可回收線程。比如說:線程池雖然創建了10個線程,但是運行的線程只有4個,線程用完之後回收,方便其他線程來使用
package com.aaa.demoThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewCachedThreadPoolDemo {
public static void main(String[] args) {
//創建的是newCachedThreadPool線程池子
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for(int i=0;i<30;i++){
int temp=i;
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("名稱:"+Thread.currentThread().getName()+"執行的是:"+temp);
}
});
}
}
}
結果如圖:
第二種:newFixedThreadPool:
創建一個定長線程池,可控制最大併發數,超出的線程數量會在外邊等待
package com.aaa.demoThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewFixedThreadPool {
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 20; i++) {
int temp=i;
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"i:"+temp);
}
});
}
}
}
結果如圖所示;
第三種:newScheduledThreadPool:
這個線程池會定時調用方法
package com.aaa.demoThread;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class NewScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
for (int i = 0; i < 10; i++) {
int temp= i;
newScheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("名稱:"+Thread.currentThread().getName()+"執行的是:"+temp);
}
}, 5, TimeUnit.SECONDS);
}
}
}
5s運行結果:
第三種:newSingleThreadPool:
這個是一個單例的線程,實戰中幾乎用到的不多
package com.aaa.demoThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class newSingleThreadPool {
public static void main(String[] args) {
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("index:" + index);
try {
Thread.sleep(200);
} catch (Exception e) {
// TODO: handle exception
}
}
});
}
}
}
如圖所示:
e).線程池的原理分析
看源碼:這裏拿newFixedThreadPool舉栗子,點擊newFixedThreadPool的execute進入ThreadPoolExcutor
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
核心:
最主要的區別就是核心線程數和最大線程數量:
a).用戶線程>核心線程數 -----> 將大於核心線程的數量放在隊列線程中 -----> 繼續執行任務 --------> 用戶線程>緩存隊列線程 ---------> 將大於最大線程數量 ------> 放在最大線程池裏邊 -----> 用戶線程>最大線程 -------> 出現異常
自己來一個畫圖:
多線程總結:
四種線程池都是基於ThreadPoolExecutor,但是是根據不同的構造方法來搞的,主要是四個參數,根據不同的構造方法來完成不同的需求。
參數說明:
CorePoolSize:核心線程池的大小。當任務來了之後就會執行任務,如果當前線程達到了CorePoolSize十週,就會給線程放在緩存池中。。
MaxnumPoolSize:線程池的最大數量。表示的是線程池中最多創建的線程池數量。
keepAliveTime:表示的當前線程池沒有變成線程執行時,保持多久時間會終止。
unit:表示的是keepAliveTime的時間單位。主要有年月日,時分秒
f).合理的配置線程數數量
主要從兩個方面來考慮線程數量
a)CPU密集 b).I/O密集
https://blog.csdn.net/wt520it/article/details/87894309
CPU密集型時,任務可以少配置線程數,大概和機器的cpu核數相當,這樣可以使得每個線程都在執行任務
IO密集型時,大部分線程都阻塞,故需要多配置線程數,2*cpu核數
操作系統之名稱解釋:
某些進程花費了絕大多數時間在計算上,而其他則在等待I/O上花費了大多是時間,
前者稱爲計算密集型(CPU密集型)computer-bound,後者稱爲I/O密集型,I/O-bound。
在實戰中學習,在快樂中成長