- 歡迎光臨 ^ V ^
JavaEE & 線程案例 & 定時器 & 線程池 and 工廠模式
1. 定時器
-
定時器,可以理解為鬧鐘
- 我們設(shè)立一個時間,時間一到,讓一個線程跑起來~
-
而Java標準庫提供了一個定時器類:
- Timer ,from java.util
1.1 定時器Timer的使用
1.1.1 核心方法schedule
- 傳入任務(wù)引用(TimerTask task)和 “定時”(long delay / ms)
-
由于TimerTask不是函數(shù)式接口,是普通的抽象類
- 所以只能用匿名內(nèi)部類,而不能用lambda表達式
- 寫法
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("好耶 ^ v ^");
}
}
},1000);
System.out.println("不好耶 T . T");
}
-
TimerTask實現(xiàn)了Runnable
- 不能傳Runnable對象過去,這屬于向下轉(zhuǎn)型~
-
- 是Runnable的一個“封裝”
- 所以,重寫run方法,合情合理~
- 只不過不能用
-
而在Timer的schedule方法內(nèi)部,則將這個線程保存起來,定時后執(zhí)行~
- 而這,有一個細節(jié),就是執(zhí)行完后,程序并沒有結(jié)束,進程并沒退出
原因是:
-
Timer內(nèi)置了一個前臺線程
- 阻止進程退出~
- 這并不是重點,其實就是timer在等待被安排下一個任務(wù)~
1.1.2 定時器管理多個線程
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
}
- 那么就安排多個任務(wù)唄~
1.1.3 定時器的使用場景
-
應(yīng)用場景特別多
- 尤其是網(wǎng)絡(luò)編程
-
而這個任務(wù)等待,不應(yīng)該是無期限的
- 超時:504 【gateway timeout】
-
定時器可以強制終止請求:瀏覽器內(nèi)部都有一個定時器,發(fā)送請求后,定時器就開始定時;若在規(guī)定時間內(nèi),響應(yīng)數(shù)據(jù)沒有返回,就會強制終止請求
-
這個方法一般在任務(wù)的run方法中調(diào)用,確定是否及時
- 這種特殊語法不是我們能理解的,并且目前我們不需要用到這個用法~
1.2 自己實現(xiàn)一個定時器
-
想法一,根據(jù)任務(wù)們的時間
-
在添入的時候,就讓他們啟動并以對應(yīng)的時間"睡下"
- 有點像睡眠排序法這個消遣的笑話~
- 顯然這個方法是不科學(xué)的,線程到達一個量級,進程必然裝不下
- 系統(tǒng)必然卡死崩掉
-
在添入的時候,就讓他們啟動并以對應(yīng)的時間"睡下"
-
想法二,根據(jù)時間,到了時間自動啟動~
- 將任務(wù)們按照時間長短排序
-
每次只看最早啟動的任務(wù)就好
- 當(dāng)然,等待時間是同步的~
- 每個任務(wù)都有在等
-
啟動,再去看接下來的任務(wù)~
- 如果兩個任務(wù)同時啟動,順序則不能確定~
是不是觸動你的DNA了?
-
沒錯,搞一個堆就好了
- 每次可見堆頂元素~
- 而小根堆堆頂正是我們這里的最早啟動的任務(wù)~
- 舊堆頂取走后,新堆頂又是剩余的最早啟動的任務(wù)~
-
而定時器的核心數(shù)據(jù)結(jié)構(gòu)就是:優(yōu)先級隊列 ===> 堆
- 而定時器可能被多線程使用,所以線程安全問題也要被保證
- 隊列為空,隊列為“滿”的時候,對操作也要有限制(不應(yīng)該有無限個任務(wù))
- 這就需要我們的阻塞隊列~
即,定時器底層就是一個阻塞優(yōu)先級隊列! ===> PriorityBlockingQueue
- 對于PriorityBlockingQueue,我這里并不會去模擬~
1.2.1 屬性
class MyTask {
public Runnable runnable;
public long time;
}
public class MyTimer {
private PriorityBlockingQueue<MyTask> tasks = new PriorityBlockingQueue<>();
}
阻塞優(yōu)先級隊列中的元素應(yīng)該有如下兩個信息:
- MyTask
- 執(zhí)行什么任務(wù)~
- 任務(wù)什么時候執(zhí)行~
1.2.2 建立一個MyTask對象
- runnable就是一個任務(wù)~
-
time是絕對時間,而不是定時時間
- 是”啟動時間“的具體時間
- 到達這個時間,任務(wù)才能運行~
- 為1970.01.01那一天的00:00:00到構(gòu)建對象時的此時此刻的毫秒數(shù)~
- 獲取當(dāng)前時間方法:
System.currentTimeMillis()
class MyTask {
public Runnable runnable;
public long time;
//絕對時間戳~
//方便判斷~
//這個不是定時時間
public MyTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.time = delay + System.currentTimeMillis();
}
}
1.2.3 schedule方法
public void schedule(Runnable runnable, long delay) {
MyTask myTask = new MyTask(runnable, delay);
tasks.put(myTask);
}
- 構(gòu)造一個myTask對象插入到隊列中~
1.2.4 構(gòu)造方法初步設(shè)計
public MyTimer() {
Thread t = new Thread(() -> {
try {
MyTask myTask = tasks.take();
long nowTime = System.currentTimeMillis();
if(myTask.time <= nowTime) {
//啟動
}else {
//不能啟動
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
-
定時器被構(gòu)造出來后,應(yīng)該就已經(jīng)啟動“母線程”
- 就應(yīng)該嘗試【take】了
- 只不過隊列為空,要阻塞等待~
- 之后通過schedule安排任務(wù)~【put】
-
啟動:
- 調(diào)用run方法
-
不能啟動:
- 將任務(wù)返回隊列
1.2.5 構(gòu)造方法最終設(shè)計
-
在構(gòu)造方法初步設(shè)計有兩個很嚴重的BUG
- 可以停止觀看去想一想~
- 優(yōu)先級對于自定義類,需要我們給“比較規(guī)則”,“優(yōu)先級規(guī)則”
- “沒有等待”以及“盲目等待”
對于1. 比較規(guī)則:
-
只需要讓MyTask實現(xiàn)比較接口
-
當(dāng)然也可以傳比較器~(lambda表達式)
-
兩種方式都OK~
-
左減右大于0
-
如果代表此對象大于該對象代表升序排列 ===> 小根堆
-
如果代表此對象小于該對象代表降序排列 ===> 大根堆
-
對于2. “沒有等待”以及“盲目等待”
-
上述代碼只會判斷一次~
- 應(yīng)該套上一個循環(huán)~
-
wait等待,喚醒起來比較方便安全
- sleep不是一個很好的選擇~
- 因為新任務(wù)的插入,要進行喚醒
- 超過限定時間,自動醒來
- wait需要有鎖,這里我把循環(huán)體整個框起來了
- 我用的是“同步鎖”
-
“盲目等待” 代表,這里放回去后,計算器又會判斷是否可啟動
- 這樣就會導(dǎo)致一段時間內(nèi),這個任務(wù)反復(fù)被拿來拿去無數(shù)次~
- 相當(dāng)于,上課時看表,一秒看一次,忙等
- 而計算機,1ms就可以看很多很多次~
-
那么我們只需要在schedule時喚醒一下,讓他才判斷一次就行了~
- 這防止新插入的任務(wù)更早而被忽略
-
大大減少判斷次數(shù)!
- 最終版:
public void schedule(Runnable runnable, long delay) {
MyTask myTask = new MyTask(runnable, delay);
tasks.put(myTask);
synchronized (locker) {
locker.notify();
}
}
private Object locker = new Object();
public MyTimer() {
Thread t = new Thread(() -> {
while(true) {
synchronized (locker) {
try {
MyTask myTask = tasks.take();
long nowTime = System.currentTimeMillis();
if(myTask.time <= nowTime) {
myTask.runnable.run();
}else {
tasks.put(myTask);
locker.wait(myTask.time - nowTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
- 別忘了啟動線程~
1.3 測試MyTimer
-
用MyTimer替換之前的Timer
-
TimeTask也可替換為Runnable,不過沒關(guān)系,向上轉(zhuǎn)型~
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
- 測試結(jié)果正常:
- 退出代碼130,是按ctrl + f2
1.4 補充
- 你可能也發(fā)現(xiàn)了,代碼之中并沒有完全保證,一個線程一定會在規(guī)定的時間后執(zhí)行
-
因為一個定時器,只能運行一個線程,沒有并發(fā)性
- 只是和main線程并發(fā)~
- 所以,如果一個線程運行時間較長,會導(dǎo)致其后的任務(wù)“被迫延時”
- 而判斷條件不是等于等于,也有這一方面原因
- 另一方面原因是,可能因為調(diào)度問題有誤差~
- 此時這個定時器,就只能起到,保證任務(wù)執(zhí)行順序的功能~
1.4.1 例子1
- 例如以下測試代碼:
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期一好耶 ^ v ^");
try {
Thread.sleep(5000);
System.out.println("已過去五秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期二好耶 ^ v ^");
}
},2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期三好耶 ^ v ^");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期四好耶 ^ v ^");
}
},4000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("星期五好耶 ^ v ^");
}
},5000);
System.out.println("今天不好耶 T . T");
}
}
- 第一個任務(wù)要花5秒,而還差1秒,第二個任務(wù)就應(yīng)該啟動~
- 而現(xiàn)象是這樣的:
- 后面的任務(wù)已經(jīng)受嚴重延遲~
1.4.2 例子2
- 如果一個任務(wù)死循環(huán)了,會導(dǎo)致后面的任務(wù)無限延期
- 就會導(dǎo)致下面這種情況:
注意:
- 這并不是我寫的定時器有問題 ,Java標準庫的定時器,就是這樣子的, 一個定時器一個時間段里只能執(zhí)行一個任務(wù)
- 現(xiàn)象跟MyTimer是一樣的
- 就是這兩個例子那樣
- 一個任務(wù)時間太長,會導(dǎo)致下一個任務(wù)延遲
- 只起“區(qū)分先后”的作用
1.5 順帶一題
問:wait的同步鎖的位置不同,結(jié)果會怎么樣?
- 例如:
- 這兩種鎖的框法不同,結(jié)果一樣嗎?
1.5.1 后者
- 重點就在于,沒有保證take與wait是原子的~
1.5.2 前者
- 保證原子性后:
2. 線程池
-
跟字符串常量池和數(shù)據(jù)庫連接池一樣
- 這個池的作用就提高效率,節(jié)省開銷~
- 即使線程很輕量,但是積少成多就不能忽略~
- 只要再池子里去拿,就要比從系統(tǒng)申請要快~
提高效率還能提高輕量化線程“協(xié)程”,Java標準庫還不支持
而線程池是一個重要的途徑~
- 從線程池里拿線程,純純的用戶態(tài)操作
- 而從系統(tǒng)上申請,就必須設(shè)計用戶態(tài)和內(nèi)核態(tài)之間的切換
- 真正的創(chuàng)建線程,是在內(nèi)核態(tài)完成的
2.1 用戶態(tài)和內(nèi)核態(tài)
-
操作系統(tǒng) = 內(nèi)核 + 配套的應(yīng)用程序
- 內(nèi)核:各種系統(tǒng)管理和驅(qū)動,而內(nèi)核就是為了支持應(yīng)用程序的
- 這里不僅僅指核心~
- 因為進程管理這是他的工作之一
- 邏輯核心們也只是他的打工人~
-
需要內(nèi)核支持,才能運行的應(yīng)用程序~
- 例如,println,打印到屏幕,需要通過硬件管理~
-
即,內(nèi)核給那么多人服務(wù),那么就不一定及時
舉個栗子:
-
去銀行打印資料,前臺可以幫你打印
- 而前臺在同時會去幫助其他人,給你打印好了還要好一會兒才給你~
-
你也可以去自助打印機打印
- 這樣的時間消耗就只會縮短在 “打印需求” 內(nèi)去消耗
-
也就是說,我們在申請線程時
-
內(nèi)核態(tài)申請 ==> 內(nèi)核要顧及進程管理和其他管理與驅(qū)動~
-
-
用戶態(tài)去拿 ==> 只需要在進程管理這個單項里去拿線程~
-
-
-
當(dāng)然,線程的誕生,還是要內(nèi)核態(tài)申請
- 放進線程池,之后在線程池里用戶態(tài)拿就好~
2.2 標準庫線程池類ExecutorService
-
Java標準庫實現(xiàn)了一個接口,ExecutorService,在進程中服務(wù)線程執(zhí)行~
- 通過這個池的服務(wù),不需要每次都申請~
-
但是這個接口不是通過new子類對象去實例化的,而是用一個靜態(tài)方法去實例化~
-
而這里的Executors類就是“工廠類”
- 這個類就是為了構(gòu)造“線程池”而存在的
- 這個類可以調(diào)用各種靜態(tài)方法
- 而這些靜態(tài)方法使用起來簡單
- 并且可以構(gòu)造各種滿足我們特殊需要的對象
2.3 工廠模式
-
“工廠”
- 即“對象工廠”,可以工廠生產(chǎn)出不同的對象
- 有員工去幫你生產(chǎn),使用簡單
- 降低使用成本
-
相同原料可以有不同產(chǎn)品,避免參數(shù)列表相同導(dǎo)致無法觸發(fā)重載
- 重要作用!
-
而工廠模式其實就是,把一個類/接口的構(gòu)造方法,交給一個“工廠類”去定義
- 即,將構(gòu)造方法打包成類
Executors工廠:
- 重點掌握
你也可以自己“開個廠”
- 就比如說,一個【堆】,泛型類是我們的自定義類
- 而我們的自定義類要我們?nèi)ヒ?guī)定比較方法
public class A {
int a1;
int a2;
int a3;
int a4;
int a5;
int a6;
}
-
假設(shè)我們A類有六個成員(都是int類型)
- 要求建立6個堆,每個堆以不同的比較規(guī)則去創(chuàng)建
- 每次創(chuàng)建都好麻煩,都要寫個比較器~
- 要求建立6個堆,每個堆以不同的比較規(guī)則去創(chuàng)建
-
只需要“開個比較器廠”,把這些構(gòu)造方法包裝起來就好~
- 以后構(gòu)造的時候,通過不同的方法名調(diào)用對應(yīng)的構(gòu)造方法~
-
比較器Comparator
-
構(gòu)造方法基本都沒有參數(shù)列表的,那么就不能用重載去解決~
- 比較器的不同主要不是因為構(gòu)造方法,而是compare被怎么重寫有關(guān)~
- compare方法重寫也只能重寫一個
-
構(gòu)造方法基本都沒有參數(shù)列表的,那么就不能用重載去解決~
2.3.1 開[A的構(gòu)造廠]
public static A createA1(int a) {
//匿名內(nèi)部類優(yōu)先捕獲全局性質(zhì)變量,這里在代碼塊內(nèi),a1就為全局性變量~
return new A() {
{
this.a1 = a;
}
};
}
public static A createA2(int a) {
return new A() {
{
this.a2 = a;
}
};
}
public static A createA3(int a) {
return new A() {
{
this.a3 = a;
}
};
}
public static A createA4(int a) {
return new A() {
{
this.a4 = a;
}
};
}
public static A createA5(int a) {
return new A() {
{
this.a5 = a;
}
};
}
public static A createA6(int a) {
return new A() {
{
this.a6 = a;
}
};
}
}
2.3.2 開[A的比較器廠]
class CreateComparatorA {
public static Comparator<A> createA1() {
return ((o1, o2) -> {
return o1.a1 - o2.a1;
});
}
public static Comparator<A> createA2() {
return ((o1, o2) -> {
return o1.a2 - o2.a2;
});
}
public static Comparator<A> createA3() {
return ((o1, o2) -> {
return o1.a3 - o2.a3;
});
}
public static Comparator<A> createA4() {
return ((o1, o2) -> {
return o1.a4 - o2.a4;
});
}
public static Comparator<A> createA5() {
return ((o1, o2) -> {
return o1.a5 - o2.a5;
});
}
public static Comparator<A> createA6() {
return ((o1, o2) -> {
return o1.a6 - o2.a6;
});
}
}
2.3.3 測試
public class A {
int a1;
int a2;
int a3;
int a4;
int a5;
int a6;
//參數(shù)列表相同無法特定構(gòu)造特定成員~
@Override
public String toString() {
return "A{" +
"a1=" + a1 +
", a2=" + a2 +
", a3=" + a3 +
", a4=" + a4 +
", a5=" + a5 +
", a6=" + a6 +
'}' + '\n';
}
public static void main(String[] args) {
PriorityQueue<A> queue1 = new PriorityQueue<>(CreateComparatorA.createA1());
PriorityQueue<A> queue2 = new PriorityQueue<>(CreateComparatorA.createA2());
PriorityQueue<A> queue3 = new PriorityQueue<>(CreateComparatorA.createA3());
PriorityQueue<A> queue4 = new PriorityQueue<>(CreateComparatorA.createA4());
PriorityQueue<A> queue5 = new PriorityQueue<>(CreateComparatorA.createA5());
PriorityQueue<A> queue6 = new PriorityQueue<>(CreateComparatorA.createA6());
queue1.offer(createA.createA1(2));
queue1.offer(createA.createA1(1));
queue1.offer(createA.createA1(4));
queue1.offer(createA.createA1(3));
queue1.offer(createA.createA1(5));
System.out.println(queue1);
}
}
- 結(jié)果:
- 確實以a1為標準~
當(dāng)然,工廠當(dāng)然不只可以生產(chǎn)構(gòu)造方法:
- 還能生產(chǎn)那些我們需要的:重復(fù)參數(shù)列表的方法
- 例如生產(chǎn)A的toString()方法~
- 不額外說了~
2.4 ExecutorService的屬性和方法
2.4.1 通過工廠類構(gòu)造
- 不給固定容量,按需創(chuàng)建線程池~
- 跟定時器有關(guān)~
最重點的一個:
- 提供固定容量的線程池構(gòu)造方法~
2.4.2 submit方法
- 提交線程~
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(() -> {
System.out.println("好耶 ^ v ^ ");
});
}
2.4.3 ThreadPoolExecutor類的屬性
- 既然是構(gòu)造ThreadPoolExecutor
- 那么它的屬性就至關(guān)重要~
下面是Java的官方文檔的內(nèi)容:
- 這個類在【util應(yīng)用應(yīng)用工具】的【concurrent并發(fā)包】中~
- 簡稱【JUC】
-
通過這個構(gòu)造方法讓我們看到很多屬性~
- 我也只講解這幾個屬性~
- 也不講這個方法咋實現(xiàn)的
- corePoolSize 和 maximumPoolSize
- corePoolSize為核心線程數(shù)
- maximumPoolSize為最大線程數(shù)
- 核心線程,即不能隨意停止的線程
-
最大線程數(shù)里【包含核心線程和“臨時線程”】
- 這個“臨時線程”就相當(dāng)于公司里的實習(xí)生
- 關(guān)鍵時期來應(yīng)急
- 這個核心線程就相當(dāng)于正式員工
- 不能隨意辭退
- 這個“臨時線程”就相當(dāng)于公司里的實習(xí)生
線程池會在任務(wù)少的空閑期,根據(jù)這些參數(shù)進行線程調(diào)整,把一些臨時線程給銷毀了~
- keepAliveTime(long) 和 unit(TimeUnit)
-
keepAliveTime 為臨時線程存活時間~
- “實習(xí)生”并不是立即被辭退
- 而是跟這個參數(shù)有關(guān)
- 允許最多活多久~
-
unit ==> 時間單位
- BlockingQueue< Runnable > workQueue
- 線程池要管理很多任務(wù)
- 通過阻塞隊列來組織~
- 方便程序員控制線程數(shù)據(jù)交互
- submit提交到這個阻塞隊列里~
- ThreadFactory threadFactory
- 線程工廠 ,跟工廠模式有關(guān)~
- 不細講
- RejectedExecutionHandler handler
- 線程池的拒絕執(zhí)行應(yīng)對策略~
- 池子滿了,繼續(xù)往里添加線程,如何應(yīng)對?如何拒絕?
- 線程池滿了是不依賴阻塞隊列的
- 這個任務(wù)要不要干最好立馬給出決策!
- 一般是空了依賴阻塞隊列~
- 還有阻塞隊列的線程安全性和解耦合性也很好
- 線程池滿了是不依賴阻塞隊列的
2.4.4 線程池的拒絕策略
- 直接拋異常 ---- 毀滅
- 哇哇大哭
- 直接拒絕 ---- 誰給我這個線程任務(wù),誰自己去完成
- 達咩達咩
- 拋棄最老任務(wù) ---- 把最先安排的任務(wù) [隊列頭] 給刪了,替換成新任務(wù)
- 做出犧牲
- 拋棄最新任務(wù) ---- 我繼續(xù)干原來的活,新的活誰都沒干
- 原封不動
2.5 模擬實現(xiàn)線程池
public class MyThreadPool {
private BlockingQueue<Runnable> pool = new LinkedBlockingQueue<>();
public void submit(Runnable runnable) throws InterruptedException {
pool.put(runnable);
}
//實現(xiàn)固定線程數(shù)的線程池
//不是容量,是確確實實的線程數(shù)
public MyThreadPool(int number) {
for (int i = 0; i < number; i++) {
Thread thread = new Thread(() -> {
Runnable runnable = null;
try {
while(true) {
runnable = pool.take();
runnable.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
}
-
我們的簡單實現(xiàn),不涉及2.4.3的屬性~
-
注意:這里的線程數(shù),是工作人數(shù),定量
- 而阻塞隊列里的線程數(shù),則是這些人做的”任務(wù)“
- 測試:
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool(10);
for (int i = 1; i <= 1000; i++) {
int id = i; //線程id,變量捕獲~
myThreadPool.submit(() -> {
System.out.println("好耶^ v ^ " + id);
});
}
- 提供固定的工作人員 * 10
- 源源不斷塞1000個任務(wù)~
- 工作人員瘋搶~
——
-
數(shù)據(jù)順序無序很正常
- 線程調(diào)度無序嘛~
-
程序還未結(jié)束
- 這是因為十個工作人員“吸血鬼”還等著任務(wù)呢~
線程池中如何體現(xiàn),“用戶態(tài)拿”:
- 線程池中有固定數(shù)量的線程,而這些線程是一開始一次性申請的
- 之后我們無需為了一個或者多個任務(wù)再去額外申請一個Thread對象了
- 只需要提交任務(wù)給線程池,讓線程池里的“空線程”去幫我們干事
-
反復(fù)用這些空線程去做任務(wù)~
- 只需要提交就行,不需要從線程池里取線程
- 我們可能沒有拿線程這個操作~
- 但是這樣就相當(dāng)于我們拿了線程做了任務(wù)~
- 只不過這個任務(wù)幾乎全自動地被線程池幫忙在一個線程里執(zhí)行了
- 而原本執(zhí)行一個任務(wù)就要我們需要自己申請個新的Thread對象
注意:
- 線程 不等于 任務(wù)
- 任務(wù)必須依托線程才能執(zhí)行
——
2.6 線程池的固定線程數(shù)的確定(理論)
-
至于線程池固定線程數(shù),設(shè)置為多少合適?
- 最好最科學(xué)的方式就是去測試!
-
cpu密集型,主要做一些計算工作,要在cpu上運行~
-
IO密集型,主要等待一些IO操作(讀寫硬盤/讀寫網(wǎng)卡),不怎么吃cpu
- 如果你的線程全是使用cpu的,那就得設(shè)置線程數(shù)少于核心數(shù)~
- 如果全是使用IO的,那就可以設(shè)置很多很多線程,遠超核心數(shù)
-
而實際情況不會這么極端,所以這個線程數(shù)一定是要看實際情況的
- 所以就要測試!
- 通過一些數(shù)據(jù)去看看哪個固定線程數(shù)是OK的
- 例如執(zhí)行時間…檢測資源使用狀態(tài)~
- 控制變量法~
文章到此結(jié)束!謝謝觀看 !
可以叫我 小馬,我可能寫的不好或者有錯誤,但是一起加油鴨??!多線程初階已經(jīng)結(jié)束~ 后續(xù)會出線程進階的博客!文章來源:http://www.zghlxwxcb.cn/news/detail-417818.html
敬請期待吧~文章來源地址http://www.zghlxwxcb.cn/news/detail-417818.html
到了這里,關(guān)于JavaEE & 線程案例 & 定時器 & 線程池 and 工廠模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!