一、線程池
線程池的一些參數:
corePoolSize:核心線程數量
maximumPoolSize:核心線程數量+臨時線程數量
上述是“java 的線程池策略”(其他語言,其他庫的線程池可能不同)
keepAliveTime :臨時線程的存活時間.臨時線程允許摸魚的最大時間
Timeunit:時間單位:秒/分鐘/毫秒...
workQueue:線程池要完成的任務隊列,每個任務都是通過Runnable來進行描述的
線程池在使用的時候,首先會創建一些線程,然后需要調用者給線程池放一些任務,這些線程就會從隊列中取出任務,并且執行里面的代碼。
(*工廠設計模式:正常創建一個對象是通過構造方法,new出來的。但是構造方法存在一定缺陷的,工廠模式就用來填補上這樣的缺陷。不用構造方法來出池化對象,通過一組靜態方法來初始化對象,靜態方法就可以起不同的名字來做區分了。)
ThreadFactory:線程工廠 這個類就是Thread類的工廠類,由于在線程池中,是需要創建很多線程的,創建出來的線程需要進行哪些初始化操作?就可以通過threadFactory來進行設定。
實際上就是通過newThread方法,針對Thread對象進行初始化,再把這個Thread對象返回出來。比如想把線程池中的線程,按照一定的規則,設置name,或者想把線程池的線程全都設置成后臺線程,或者都設置xxx為優先級。
例如,我想要將笛卡爾坐標系下的點轉化為極坐標下的點表示,那么——
然而,多個版本的構造方法,需要“重載”,而重載要求方法名稱相同但是參數列表不能相同,這里的參數列表相同,因此無法構成重載。那么此時的解決方法也就是:
這就是用來構造Point對象的類,也被稱為“工廠類”
RejectedExecutionHandler:拒絕策略
線程池,有任務列表、阻塞隊列,當任務隊列滿了的時候,如果再次添加新的任務,會發生什么呢?
正常來說,除非特殊說明,我們寫的代碼是不希望有這種突發性的阻塞的,因為這種阻塞稍不留神可能就對程序造成不可預估的影響。
因此直接讓添加任務的線程阻塞,其實是不太好的,不太好就意味著要有其他的方法——因此標準庫的線程池就引入了“拒絕策略”
正常情況下,是線程池里面的線程執行任務的,但是現在線程池里面的線程忙不過來了,就只能由調用者自己找個線程來執行任務
標準庫中提供了一個對ThreadPoolExecutor進行了封裝的簡化版本Executors,本質上也是工廠類,提供了一些工廠方法,對上述的ThreadPoolExecutor進行不同的初始化。
這里的工廠方法,就是在創建不同風格的線程池。
package Thread;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class demo39 {public static void main(String[] args) {//固定線程數目的線程池,核心線程數和最大線程數都是4ExecutorService executorService = Executors.newFixedThreadPool(4);//核心線程數設置為0,最大線程數設為一個Int最大值這樣的線程池ExecutorService executorService1 = Executors.newCachedThreadPool();//固定只有一個線程的線程池(這個東西確實用的不多,可以通過這種方式來代替創建線程的方式)ExecutorService executorService2 = Executors.newSingleThreadExecutor();//這個線程池,本質上是一個定時器,放到這個線程池中的任務,會在一定時間之后執行ExecutorService executorService3 = Executors.newScheduledThreadPool(4);}}
現在我們來讓這個線程池來執行一些任務,為了更清晰地看出這個任務做了10次,我們來對i進行打印。然而這里不能直接這么些,會出現“變量捕獲”的問題。
這里通過這種方法來進行解決。
有些公司更加想要完全體的ThreadPoolExecutor,可控性更強。
如果隊列中沒有其他的任務,take就會阻塞,一直阻塞到其他線程執行submit為止
package Thread;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;class MyFixedThreadPool{ // 固定線程池。private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>(); // 阻塞隊列,用于存放任務。public MyFixedThreadPool(int n){ // 構造方法,初始化線程池。for(int i = 0; i < n; i++){ // 創建n個線程。Thread t = new Thread(()->{ // 創建線程。try {while (true) {Runnable task = blockingQueue.take(); // 從阻塞隊列中取出任務。task.run(); // 執行任務。}} catch (Exception e) {// TODO: handle exceptione.printStackTrace();} });t.start(); }
}public void submit(Runnable task) throws Exception{ // 提交任務。// 1. 將任務放入阻塞隊列。blockingQueue.put(task);}
}public class demo40 {public static void main(String[] args) throws Exception {// TODO Auto-generated method stubMyFixedThreadPool myFixedThreadPool = new MyFixedThreadPool(100); // 創建線程池。for(int i = 0; i < 10; i++){ // 提交1000個任務。int finalI = i; // 閉包。myFixedThreadPool.submit(()->{ // 提交任務。System.out.println("任務" + finalI + "正在執行"); // 打印任務正在執行。});}}}
二、定時器
定時器在java編程中是一個非常終于熬的組件。
阻塞隊列,太重要了,太常用了,在實際工作中,會把阻塞隊列單獨封裝成一個/一組服務器
定時器也是類似,也會被單獨封裝成一個/一組服務器,類似于鬧鐘。
為什么需要定時器呢?——編程中有些任務,不需要立即執行,而是要等一會兒,等到到一定時間的時候再去執行。
Java標準庫提供了Timer這樣的類,可以通過他來實現簡單的定時器。
一開始輸出:程序啟動,過了4s之后輸出“定時器執行任務”
自行實現一個定時器
package Thread;import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;private long delay;private long time ;public MyTimerTask(Runnable task, long delay){this.task = task;this.time = delay + System.currentTimeMillis();}public Runnable getTask(){return task;}public long getTime(){return time;}public int compareTo(MyTimerTask o){ // 比較兩個任務的時間,誰的時間小誰排在前面。return (int)(this.time - o.time); // 比較兩個任務的時間,誰的時間小誰排在前面。} // 比較兩個任務的時間,誰的時間小誰排在前面。
}//自己實現定時器
//定時器能夠同時管理多個任務
//有一定的數據結構來組織這多個任務
//ArrayList不太合適
//更好的選擇是優先級隊列class MyTimer{private PriorityQueue<MyTimerTask> queue = new PriorityQueue<MyTimerTask>();private static Object locker = new Object();public MyTimer(){//創建線程Thread t = new Thread(()->{ // 線程的任務是不斷地從隊列中取出任務,并執行。while (true) {try { // 捕獲異常。synchronized(locker){ // 出隊列。if(queue.isEmpty()){locker.wait();}MyTimerTask task = queue.peek(); // 取出隊列中的第一個任務。long curTime = System.currentTimeMillis(); // 獲取當前時間。if(curTime < task.getTime()){ // 如果當前時間小于任務的時間,說明任務還沒有到執行時間。locker.wait(task.getTime() - curTime);}else{queue.poll(); // 取出隊列中的第一個任務。task.getTask().run(); // 執行任務。}}} catch (InterruptedException e) { // 捕獲異常。e.printStackTrace(); // 打印異常棧。}}});t.start(); // 啟動線程。}public void schedule(Runnable task, long delay){ synchronized(locker){ // 入隊列。queue.offer(new MyTimerTask(task, delay)); // 入隊列。locker.notify(); // 喚醒線程。} // 入隊列。}
}public class demo42 {public static void main(String[] args) {MyTimer timer = new MyTimer(); // 創建定時器。timer.schedule(new Runnable() {@Overridepublic void run() { // 任務的執行邏輯。System.out.println("定時任務 3000"); // 打印出 hello world。}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() { // 任務的執行邏輯。System.out.println("定時任務 2000"); // 打印出 hello world。}}, 2000); timer.schedule(new Runnable() {@Overridepublic void run() { // 任務的執行邏輯。System.out.println("定時任務 1000"); // 打印出 hello world。}}, 1000);}}