線程等待喚醒機制
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));