七、線程池、Lambda
1.1基本概念:
線程池:其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多的資源。
1.2線程池的好處:
1.降低資源消耗。減少了創建和銷毀線程得我次數,每個工作線程都可以被重復利用,可執行多個任務。
2.提高了響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行
3.提高線程的可管理學。可以根據系統的承受能力,調用線程池中工作線程的數目,防止因為消耗過多的內存,而把服務器累趴下。
1.3線程池的使用:
Java中的線程池的頂級接口是java.util.concurrent.Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具。真正的線程池接口是 java.util.concurrent.ExecutorService,要配置一個線程池是比較復雜的,尤其是對於線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的,因此在Java.util.concurrent.Executors線程工廠類里面提供了一些靜態工廠,生成一些常用的線程池。官方建議使用Executors工程類創建線程池對象。
ExecutorExecutors類中有個創建線程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads): 返回線程池對象。(創建的有界線程池,也就是池中的線程個數可以指定最大數量)
獲得到一個線程池對象,那么怎么使用呢,在這里定義了一個使用線程池對象的方法如下:
*public Futrue<?> submit (Runnable task): 獲取線程池中的某一個線程對象,並執行
Future 接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用
使用線程池線程對象的步驟:
1.創建線程池對象。
2.創建Runnable接口子類對象。(task)
3.提交Runnable接口子類對象。(take task)
4.關閉線程池(一般不做)
Runnable實現類代碼:
public class MyRunnable implements Runnable {
public void run() {
System.out.println("我要一個教練");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教練來了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教練回到了游泳池");
}
}
測試類:
public class ThreadPoolDemo {
public static void main(String[] args) {
// 創建線程池對象
ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
// 創建Runnable實例對象
MyRunnable r = new MyRunnable();
//自己創建線程對象的方式
// Thread t = new Thread(r);
// t.start(); ---> 調用MyRunnable中的run()
// 從線程池中獲取線程對象,然后調用MyRunnable中的run()
service.submit(r);
// 再獲取個線程對象,調用MyRunnable中的run()
service.submit(r);
service.submit(r);
// 注意:submit方法調用結束后,程序並不終止,是因為線程池控制了線程的關閉。
// 將使用完的線程又歸還到了線程池中
// 關閉線程池
//service.shutdown();
}
}
八 、Lambda
借助Java8的全新語法,顯示用Lambda展示簡潔的實現Runnable開啟多線程
public class Demo02LambdaRunnable{
public static void main(String[] args){
new Thread (() -> System.out.println("子線程開啟!")).start()
}
}
public class Demo04ThreadNameless {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println("多線程任務執行!");
}
}).start();
}
}
8.1語義分析
仔細分析該代碼中的語義,Runnable
接口只有一個run
方法的定義:
-
public abstract void run();
即制定了一種做事情的方案(其實就是一個函數):
-
無參數:不需要任何條件即可執行該方案。
-
無返回值:該方案不產生任何結果。
-
代碼塊(方法體):該方案的具體執行步驟。
其中核心代碼:() -> System.out.println("子線程開啟!")
我們先來分析一下匿名內部類的好處與弊端:
一方面,匿名內部類可以幫助我們省去實現類的定義;
另一方面,匿名內部類的語法--確實太復雜
8.2Lambda表達式介紹:
() -> System.out.println("多線程任務執行了!")
~其中() 就是run方法的參數(無),代表不需要任何條件:
~中間的一個箭頭代表將前面的參數傳遞給后面的代碼:
~后面的輸出語句即業務邏輯代碼。
8.3標准格式:
一些參數
一個箭頭
一段代碼
(參數類型 參數名稱) ->{代碼語句}
8.4格式說明:
~小括號內的語法與傳統方法參數列表一致:無參數則留空;多個參數則用逗號分割
~ -> 是新引入的語法格式,代表指向動作
~大括號內的語法與傳統方法要求基本一致
8.5 Lambda的使用前提
Lambda的語法非常簡潔,完全沒有面向對象復雜的束縛,但是在使用時有幾個問題需要特別注意:
1.使用Lambda必須具有接口,且要求接口中有且僅有一個抽象方法,無論是JDK內置的Runnable,Comparator接口還是自定義的接口,只有當接口中的抽象方法存在且唯一時,才可使用Lambda
2.使用Lambda必須具有上下文推斷。也就是方法參數或局部變量類型必須為Lambda對應的接口類型,才能使用Lambda作為該接口的實例
tips:有且僅有一個抽象方法的接口,稱為 函數式接口
問題1: 請描述什么是線程池。 答: 線程池:其實就是一個容納多個線程的容器,其中的線程可以反復使用, 省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。 問題2: 請描述合理利用線程池能夠帶來的三個好處。 答: 1. 降低資源消耗。減少了創建和銷毀線程的次數,每個工作線程都可以被重復利用,可執行多個任務。 2. 提高響應速度。當任務到達時,任務可以不需要的等到線程創建就能立即執行。 3. 提高線程的可管理性。可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的內存, 而把服務器累趴下(每個線程需要大約1MB內存,線程開的越多,消耗的內存也就越大,最后死機)。 問題3: 請列舉Lambda語法的省略規則 答: 在Lambda標准格式的基礎上,使用省略寫法的規則為: 1. 小括號內參數的類型可以省略; 2. 如果小括號內有且僅有一個參,則小括號可以省略; 3. 如果大括號內有且僅有一個語句,則無論是否有返回值,都可以省略大括號、return關鍵字及語句分號。 問題4: 請列舉Lambda表達式的3個組成部分,並解釋說明。 答: Lambda標准格式 Lambda省去面向對象的條條框框,格式由3個部分組成:一些參數,一個箭頭,一段代碼。 Lambda表達式的標准格式為:格式說明: 1. 小括號內的語法與傳統方法參數列表一致:無參數則留空;多個參數則用逗號分隔。 2. ->是新引入的語法格式,代表指向動作。 3. 大括號內的語法與傳統方法體要求基本一致。 問題5: 請描述Lambda的使用前提 答: Lambda的語法非常簡潔,完全沒有面向對象復雜的束縛。但是使用時有幾個問題需要特別注意: 1. 使用Lambda必須具有接口,且要求接口中有且僅有一個抽象方法。無論是JDK內置的 Runnable 、 Comparator 接口還是自定義的接口,只有當接口中的抽象方法存在且唯一時,才可以使用Lambda。 2. 使用Lambda必須具有上下文推斷。也就是方法的參數或局部變量類型必須為Lambda對應的接口類型,才能使用Lambda作為該接口的實例。