18.Java語言執行緒池和Lambda表示式
執行緒等待喚醒機制
1.執行緒間的通訊:
一個程式完成某個任務,需要多個執行緒協調,就需要執行緒之間存在“通訊”,比如生產者和消費者,只有生產了才能被消費。當生產者生產完成才能告知消費者可以消費,那麼告知的過程就是執行緒間的通訊。
2.等待與喚醒機制:
1)."等待與喚醒機制”就是“執行緒間通訊”的一種體現。
2).工作形式:
1).一個執行緒做一些“準備性工作”。
2).另一個執行緒做正常的工作。由於兩個執行緒是“無序的”,很有可能“第二個執行緒工作時,第一個執行緒的準備工作還沒
有做好,這時就需要第二個執行緒要主動“等待”,然後第一個執行緒進入開始做準備,準備工作做好後,再喚醒第二個
執行緒開始正常工作。
喚醒機制_生產者與消費者
執行緒通訊_包子鋪 -----生產者生產包子,放在包子鋪,消費者到包子鋪取包子,只有生產者生產完包子通知消費者,消費者才
可以到包子鋪獲取包子,否則只能無限等待
包子鋪類:
import java.util.ArrayList; import java.util.List; public class BaoZiPu{ List<String> list = new ArrayList<>(); Object obj = new Object(); public void setBaozi(){ synchronized (obj){ list.add("包子"); obj.notifyAll(); } } public String getBao() throws InterruptedException { synchronized (obj){ if(list.size() == 0){ System.out.println("訪問的執行緒需要等待...."); obj.wait(); System.out.println("訪問的執行緒被喚醒......"); } String s = list.get(0); list.remove(s); return s; } } }
獲取包子
public class GetBaozi extends Thread{ BaoZiPu baoZiPu; public GetBaozi(BaoZiPu baoZiPu) { this.baoZiPu = baoZiPu; } @Override public void run() { while (true){ try { System.out.println(baoZiPu.getBao()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
生產包子
public class SetBaozi extends Thread {
BaoZiPu baoZiPu;
public SetBaozi(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while (true) {
baoZiPu.setBaozi();
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測試類:
public class Demo {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
GetBaozi getBaozi = new GetBaozi(baoZiPu);
SetBaozi setBaozi = new SetBaozi(baoZiPu);
getBaozi.start();
setBaozi.start();
}
}
執行緒池
儲存了若干多的“執行緒物件”的一個“容器”。 執行緒池類:ExecutorService這個執行緒池就可以快取大量的執行緒物件,並可以反覆的重用它們。
1.執行緒池思想概述
1).對於一個執行緒物件“只能啟動一次”,若想第二次再用,就需要再次建立一個執行緒物件,但如果建立執行緒物件很耗時,這
樣如果程式中要反覆的使用同一個執行緒,整個程式的效率就會很低。
2).執行緒池的思想:在程式啟動時,會先建立若干多的執行緒物件,並存儲到一個容器中,作為“執行緒池”。如果有需要時,取
出一個執行緒物件,並執行它。執行完畢,執行緒池會回收這個執行緒物件,如果再次需要,可以再次取出,並執行它。所以,
執行緒池中的執行緒物件是“可以反覆重用”的。這樣就避免的反覆的建立執行緒物件,從而提高程式的執行效率。
2.執行緒池的使用
Java裡面執行緒池的頂級介面是 java.util.concurrent.Executor ,但是嚴格意義上講Executor 並不是一個執行緒池,而只是一
個執行執行緒的工具。真正的執行緒池介面是java.util.concurrent.ExecutorService 。
Executors類中有個建立執行緒池的方法如下:
a).public static ExecutorService newFixedThreadPool(int nThreads) :返回執行緒池物件。(建立的是有界執行緒池,也就是池
中的執行緒個數可以指定最大數量)
b).public Future<?> submit(Runnable task) :獲取執行緒池中的某一個執行緒物件,並執行
3.執行緒池的特點
如果建立一個執行緒需要五秒鐘,不用執行緒池,每建立一個執行緒就需要五秒,用了執行緒池,因為執行緒池執行後,會將此執行緒
物件"快取",可以重用,那麼再次建立就不需要時間。
示例:
public class MyThread extends Thread {
public MyThread() {
for (int i = 0; i < 5; i++) {
System.out.println("等待中..." + (i + 1) + "秒");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
System.out.println("執行緒執行啦");
}
}
測試類:
public class Demo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
myThread = new MyThread();
myThread.start();
}
}
Lambda表示式
1.冗餘的Runnable程式碼
方式一是建立Runnable的實現類的物件,通過Thread的建構函式建立執行緒。
方式二是用Runnable的匿名內部類建立執行緒。
public class Demo {
public static void main(String[] args) {
//1.方式一:製作Runnable子類的方式
MyRunnable myRunnable = new MyRunnable();
Thread t = new Thread(myRunnable);
t.start();
//2.方式二:匿名內部類的方式
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
});
t2.start();
}
}
2.Lambda表示式的寫法
3.程式設計思想轉換及函數語言程式設計思想概述
1).程式設計思想轉換:將“以什麼形式做”,轉換為“怎樣做”。
2).“函式式”程式設計思想:當呼叫的方法需要一個“介面”型別時,就可以考慮直接傳入一個代替的“函式(方法)”即可,其它無用的語句可以省略。
4.Lambda表示式的使用
1).使用前提:具備以下條件,才可以使用Lambda。
1).首先需要的是一個“介面型別”;
2).而且這個介面中有,且只有一個“抽象方法”--函式式介面;
2).標準格式(三部分)
第一部分:一對小括號--形參;
第二部分:一個右箭頭:->
第三部分:一對大括號--方法體;
3).標準格式的的寫法
無參 ()->{//方法體}
有參 (int a,int b)->{//方法體}
方法體內按照正常程式碼格式
5.Lambda表示式的省略格式和原則
1).示例:
//Lambda表示式--完整格式
fun((int x, int y) -> {return x + y;}, 10, 20);
//簡寫的Lambda
fun((x, y) -> x + y, 10, 20);
2).省略規則:
1).形參:Lambda中的"形參型別”都可以省略;
fun((x) -> {return x * x;});
2).形參:如果只有一個形參,形參型別和小括號都可以省略
(要省略小括號,必須省略資料型別)
fun(x -> {return x * x;});
3).方法體:如果方法體中只有一條語句,可以應用省略規則;
A).有返回值:可以省略大括號、return關鍵字、語句後的分號
(要省全省,要用全用)
fun(x -> x * x);
B).無返回值:可以省略大括號、語句後的分號;
(要省全省,要用全用)
fun(x -> System.out.println("x = " + x));