Java20-day11【实现多线程(进程、线程-调度-控制-生命周期)、线程同步(同步代码块、线程安全、Lock)、生产者消费者(模式概述、案例)】
- 視頻+資料【鏈接:https://pan.baidu.com/s/1MdFNUADVSFf-lVw3SJRvtg? ?提取碼:zjxs】
- Java基礎--學習筆記(零起點打開java世界的大門)--博客匯總表
? ?? ??
目? ?錄
01_進程和線程
1.1、進程
1.2、線程
02_繼承Thread類的方式實現多線程
1.3、多線程的實現方式
03_設置和獲取線程名稱
1.4、設置和獲取線程名稱
04_線程優先級
1.5、線程調度
05_線程控制
1.6、線程控制
06_線程的生命周期
1.7、線程生命周期
07_實現Runnable接口的方式實現多線程
1.8、多線程的實現方式
08_賣票
案例:賣票
09_賣票案例的思考
2.1、賣票案例的思考
10_同步代碼塊解決數據安全問題
2.2、賣票案例數據安全問題的解決
2.3、同步代碼塊
11_同步方法解決數據安全問題
2.4、同步方法
12_線程安全的類
2.5、線程安全的類
13_Lock鎖
2.6、Lock鎖
14_生產者和消費者模式概述
3.1、生產者和消費者模式概述
15_生產者和消費者案例
3.2、生產者和消費者案例
01_進程和線程
1.1、進程
進程:是正在運行的程序。
- 是系統進行資源分配和調用的獨立單位。
- 每一個進程都有它自己的內存空間和系統資源。
線程依賴于進程而存在。
在一個進程內部,可以執行一個或多個任務,每個任務可以看成一個線程。
1.2、線程
線程:是進程中的單個順序控制流,是一條執行路徑。
- 單線程:一個進程如果只有一條執行路徑,則稱為單線程程序。
- 多線程:一個進程如果有多條執行路徑,則稱為多線程程序。
舉例:
- 記事本程序
- 掃雷程序
????掃雷:多線程
02_繼承Thread類的方式實現多線程
1.3、多線程的實現方式
方式1:繼承Thread類
- 定義一個類MyThread繼承Thread類
- 在MyThread類中重寫run()方法:MyThread類中可能還有其它的代碼,并不是所有的代碼都要被線程執行。區分可以被線程執行的代碼,Java提供了run()方法,用來封裝被線程執行的代碼。
- 創建MyThread類的對象
- 啟動線程
兩個小問題:
- 為什么要重寫run()方法?
因為run()是用來封裝被線程執行的代碼。
- run()方法和start()方法的區別?
run():封裝線程執行的代碼,直接調用,相當于普通方法的調用。
start():啟動線程;然后由JVM調用此線程的run()方法。
03_設置和獲取線程名稱
1.4、設置和獲取線程名稱
Thread類中設置和獲取線程名稱的方法:
- void setName(String name):將此線程的名稱更改為等于參數name。
- String getName():返回此線程的名稱。
- 通過構造方法也可以設置線程名稱。
如何獲取main()方法所在的線程名稱?
- public static Thread currentThread():返回對當前正在執行的線程對象的引用。
Thread部分源碼:
private String name;public Thread() {this(null, null, "Thread-" + nextThreadNum(), 0); }public Thread(String name) {this(null, null, name, 0); }public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {this(group, target, name, stackSize, null, true); }private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {this.name = name; }public final synchronized void setName(String name) {this.name = name; }public final String getName() {return name; }private static int threadInitNumber; //0,1,2 private static synchronized int nextThreadNum() {return threadInitNumber++;//0,1,... }使用帶參構造方法,需要在自己定義的MyThread類中 定義 帶參構造方法,通過super()訪問父類的帶參構造方法:
獲取main()方法 當前正在執行的線程對象的名稱:【名為main的線程】
04_線程優先級
1.5、線程調度
線程調度有兩種調度方式:
- 分時調度模型:所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間片。
- 搶占式調度模型:優先讓優先級高的線程使用CPU,如果線程的優先級相同,那么會隨機選擇一個,優先級高的線程獲取的CPU時間片相對多一些。
Java使用的是搶占式調度模型。
隨機性:
假如計算機只有一個CPU,那么CPU在某一個時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,才可以執行指令。所以說多線程程序的執行是有隨機性,因為誰搶到CPU的使用權是不一 定的。
Thread類中設置和獲取線程優先級的方法(優先級相關方法):
- public final int getPriority():返回此線程的優先級。
- public final void setPriority(int newPriority):更改此線程的優先級。線程默認優先級是5;線程優先級的范圍是:1-10。
線程默認優先級是5;線程優先級的范圍是:1-10
線程優先級高僅僅表示線程獲取的CPU時間片的幾率高,但是要在次數比較多,或者多次運行的時候才能看到你想要的效果。
05_線程控制
1.6、線程控制
如果有一個線程對象調用了join()方法,其它的線程必須等這個線程執行完畢,其它的線程才有機會執行。
如果主線程執行完畢,剩下的線程全是守護線程的情況下,不會等到守護線程全部執行完,因為Java虛擬機會退出。
sleep()演示:
join()演示:?
setDaemon()演示:
“劉備”線程執行完畢之后,Java虛擬機退出(“關羽”線程、“張飛”線程 停止),需要一定的時間!
06_線程的生命周期
1.7、線程生命周期
線程一共有五種狀態,線程在各種狀態之間轉換。? ?線程生命周期:線程從生到死的過程。
07_實現Runnable接口的方式實現多線程
1.8、多線程的實現方式
方式2:實現Runnable接口:
多線程的實現方案有兩種:
- 繼承Thread類
- 實現Runnable接口
相比繼承Thread類,實現Runnable接口的好處:
- 避免了Java單繼承的局限性。
- 適合多個相同程序的代碼去處理同一個資源的情況,把線程和程序的代碼、數據有效分離,較好的體現了面向對象的設計思想。
MyRunnable沒有繼承Thread類的好處:MyRunnable將來可以有自己的父類(不影響繼承其它類);可將MyRunnable看成一個資源,由多個線程去使用。
08_賣票
?
案例:賣票
? ??
09_賣票案例的思考
2.1、賣票案例的思考
剛才講解了電影院賣票程序,好像沒有什么問題。但是在實際生活中,售票時出票也是需要時間的,所以,在出售一張票的時候,需要一點時間的延遲,接下來我們去修改賣票程序中賣票的動作:每次出票時間100毫秒,用sleep()方法實現。
賣票出現了問題
- 相同的票出現了多次。
- 出現了負數的票。
問題產生原因
- 線程執行的隨機性導致的。
10_同步代碼塊解決數據安全問題
2.2、賣票案例數據安全問題的解決
為什么出現問題?(安全問題出現的條件)(這也是我們判斷多線程程序是否會有數據安全問題的標準)
- 是否是多線程環境
- 是否有共享數據
- 是否有多條語句操作共享數據
如何解決多線程安全問題呢?
- 基本思想:讓程序沒有安全問題的環境。
怎么實現呢?
- 把多條語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可。
- Java提供了同步代碼塊的方式來解決。
2.3、同步代碼塊
鎖多條語句操作共享數據,可以使用同步代碼塊實現。
- 格式:
synchronized(任意對象) {
? ? ? 多條語句操作共享數據的代碼
}
- synchronized(任意對象):就相當于給代碼加鎖了,任意對象就可以看成是一把鎖。
同步的好處和弊端:
- 好處:解決了多線程的數據安全問題。
- 弊端:當線程很多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。
(把多條語句操作共享數據的代碼給鎖起來)使用同一把鎖🔒:
11_同步方法解決數據安全問題
2.4、同步方法
同步方法的格式
同步方法:就是把synchronized關鍵字加到方法上。
- 格式:
修飾符 synchronized 返回值類型 方法名(方法參數) { 方法體; }
同步方法的鎖對象是什么呢?
- this
靜態同步方法
同步靜態方法:就是把synchronized關鍵字加到靜態方法上。
- 格式:
修飾符 static synchronized 返回值類型 方法名(方法參數) { 方法體; }
同步靜態方法的鎖對象是什么呢?
- 類名.class
if中的代碼塊與else中的代碼塊,完全一樣!程序不會有問題!
運行結果同上。
同步方法:就是把synchronized關鍵字加到方法上。相當于對方法的內部進行鎖定。this代表本類。
? ?
同步方法的鎖對象是什么呢?this
? ?
if代碼塊加鎖,static靜態方法沒有加鎖!--> 報錯!--> 給靜態方法加鎖!
同步靜態方法:就是把synchronized關鍵字加到靜態方法上。
if代碼塊中加鎖(this:指代非靜態方法的鎖對象)
12_線程安全的類
2.5、線程安全的類
StringBuffer
- 線程安全,可變的字符序列。
- 從版本JDK 5開始,被StringBuilder替代。通常應該使用StringBuilder類,因為它支持所有相同的操作,但它更快,因為它不執行同步。
Vector
- 從Java 2平臺v1.2開始,該類改進了List接口,使其成為Java Collections Framework的成員。與新的集合實現不同,Vector被同步。如果不需要線程安全的實現,建議使用ArrayList代替Vector。
Hashtable
- 該類實現了一個哈希表,它將鍵映射到值。任何非null對象都可以用作鍵或者值。
- 從Java 2平臺v1.2開始,該類進行了改進,實現了Map接口,使其成為Java Collections Framework的成員。與新的集合實現不同,Hashtable被同步。如果不需要線程安全的實現,建議使用HashMap代替Hashtable。
13_Lock鎖
2.6、Lock鎖
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock。
Lock實現提供比使用synchronized方法和語句可以獲得更廣泛的鎖定操作。
Lock中提供了獲得鎖和釋放鎖的方法:
- void lock() 獲得鎖
- void unlock() 釋放鎖
Lock是接口不能直接實例化,這里采用它的實現類ReentrantLock來實例化。
ReentrantLock的構造方法
- ReentrantLock():創建一個ReentrantLock的實例
?
????
14_生產者和消費者模式概述
3.1、生產者和消費者模式概述
15_生產者和消費者案例
3.2、生產者和消費者案例
? ??
Box.java:
package com.itheima_12;public class Box {//定義一個成員變量,表示第x瓶奶private int milk;//定義一個成員變量,表示奶箱的狀態private boolean state = false; // 默認沒有牛奶🥛//提供存儲牛奶和獲取牛奶的操作public synchronized void put(int milk) {//如果有牛奶,等待消費if (state) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果沒有牛奶,就生產牛奶this.milk = milk;System.out.println("送奶工將第" + this.milk + "瓶奶放入奶箱");//生產完畢之后,修改奶箱狀態state = true;//喚醒其他等待的線程notifyAll();}public synchronized void get() {//如果沒有牛奶,等待生產if (!state) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果有牛奶,就消費牛奶System.out.println("用戶拿到第" + this.milk + "瓶奶");//消費完畢之后,修改奶箱狀態state = false;//喚醒其他等待的線程notifyAll();} }蟹蟹觀看~
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Java20-day11【实现多线程(进程、线程-调度-控制-生命周期)、线程同步(同步代码块、线程安全、Lock)、生产者消费者(模式概述、案例)】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2014年 第5届 蓝桥杯 Java B
- 下一篇: java美元兑换,(Java实现) 美元