JavaSe之多線程

一、多線程基本了解

1、多線程基本知識

1.進程:進入到內存中執行的應用程序
2.線程:內存和CPU之間開通的通道->進程中的一個執行單元
3.線程作用:負責當前進程中程序的運行.一個進程中至少有一個線程,一個進程還可以有多個線程,這樣的應用程序就稱之為多線程程序
4.簡單理解:進程中的一個功能就需要一條線程去執行

在這里插入圖片描述

2、并發和并行

1.并行:并行:在同一個時刻,有多個指令在多個CPU上(指多核即幾核幾線程)執行(好比是多個人做不同的事兒)
比如:多個廚師在炒多個菜
2.并發:并發:在同一個時刻,有多個指令在單個CPU上(交替)執行
比如:一個廚師在炒多個菜

細節:1.之前CPU是單核,但是在執行多個程序的時候好像是在同時執行,原因是CPU在多個線程之間做高速切換2.現在咱們的CPU都是多核多線程的了,比如2核4線程,那么CPU可以同時運行4個線程,此時不用切換,但是如果多了,CPU就要切換了,所以現在CPU在執行程序的時候并發和并行都存在

3、CPU調度

1.分時調度:讓所有的線程輪流獲取CPU使用權,并且平均分配每個線程占用CPU的時間片
2.搶占式調度:多個線程搶占CPU使用權,哪個線程優先級越高,先搶到CPU使用權的幾率就大,但是不是說每次先搶到CPU使用權的都是優先級高的線程,只是優先級高的線程先搶到CPU使用權的幾率會大一些 -> java代碼(搶占式調度)

4、主線程介紹

主線程:為main方法服務的線程

二、創建線程的方式(重點)

1、第一種方式_extends Thread

1.定義一個類,繼承Thread類
2.重寫Thread類中的run方法,設置線程任務(該線程要做的事兒)
3.創建自定義線程類對象
4.調用Thread類中的start方法start()開啟線程,jvm會自動執行run方法

定義線程對象

public class Day15Thread extends Thread {@Overridepublic void run(){for (int i = 0; i < 5; i++) {System.out.println("MyThread執行了:"+i);}}}

定義主線程

public class Day15ThreadText {public static void main(String[] args) {Day15Thread t1 = new Day15Thread();//t.run();t1.start();//開啟線程,jvm自動執行run方法for (int i = 0; i < 5; i++) {System.out.println("main...執行了"+i);}}
}運行結果:
MyThread執行了:0
MyThread執行了:1
main...執行了0
main...執行了1
main...執行了2
MyThread執行了:2
MyThread執行了:3
MyThread執行了:4
main...執行了3
main...執行了4

注意:如果直接調用run方法,并不代表將線程開啟,僅僅是簡單的調用方法 > > 只有調用start方法,線程才會真正開啟

2、多線程在內存中的運行原理

首先會內存會開啟一個棧供main方法執行,此時main方法中的start()方法被調用,調用后內存會重新開啟一個棧供新的線程運行

3、Thread中常用方法

void start() -> 開啟線程,jvm自動調用run方法
void run()  -> 設置線程任務,這個run方法是Thread重寫的接口Runnable中的run方法
String getName()  -> 獲取線程名字
void setName(String name) -> 給線程設置名字
static Thread currentThread() -> 獲取正在執行的線程對象(此方法在哪個線程中使用,獲取的就是哪個線程對象)
static void sleep(long millis)->線程睡眠,超時后自動醒來繼續執行,傳遞的是毫秒值

定義自定義線程

public class Day15Thread extends Thread {@Overridepublic void run(){for (int i = 0; i < 5; i++) {try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName()+"執行了:"+i);}}
}

定義主線程

public class Day15ThreadText {public static void main(String[] args) throws InterruptedException {Day15Thread t1 = new Day15Thread();//t.run();t1.setName("劉大膽");t1.start();//開啟線程,jvm自動執行run方法for (int i = 0; i < 5; i++) {Thread.sleep(1000L);System.out.println("主線程名稱為"+Thread.currentThread().getName()+i);}}
}

> 1.父類方法拋異常了,子類重寫之后可拋可不拋 > > 父類方法沒有拋異常,子類重寫之后不要拋 > > 2.重寫了run方法之后,由于Thread中的run方法沒有throws,那么我們重寫run之后就不能throws,只能try

4、Thread其他方法

void setPriority(int newPriority)   -> 設置線程優先級,優先級越高的線程,搶到CPU使用權的幾率越大,但是不是每次都先搶到int getPriority()  -> 獲取線程優先級void setDaemon(boolean on)  -> 設置為守護線程,當非守護線程執行完畢,守護線程就要結束,但是守護線程也不是立馬結束,當非守護線程結束之后,系統會告訴守護線程人家結束了,你也結束吧,在告知的過程中,守護線程會執行,只不過執行到半路就結束了static void yield() -> 禮讓線程,讓當前線程讓出CPU使用權void join() -> 插入線程或者叫做插隊線程
4.1 線程優先級
public class Day15ThreadText {public static void main(String[] args) throws InterruptedException {Day15Thread t1 = new Day15Thread();//t.run();t1.setName("劉大膽");Day15Thread t2 = new Day15Thread();t2.setName("超級劉大膽");t1.setPriority(Thread.MIN_PRIORITY);  //設置優先級t2.setPriority(Thread.MAX_PRIORITY);t1.start();//開啟線程,jvm自動執行run方法t2.start();System.out.println(t1.getPriority()); //獲取優先級System.out.println(t2.getPriority());for (int i = 0; i < 5; i++) {System.out.println("主線程名稱為"+Thread.currentThread().getName()+i);}}
}
輸出結果:
1
10
超級劉大膽執行了:0
主線程名稱為main0
超級劉大膽執行了:1
主線程名稱為main1
超級劉大膽執行了:2
主線程名稱為main2
超級劉大膽執行了:3
主線程名稱為main3
超級劉大膽執行了:4
主線程名稱為main4
劉大膽執行了:0
劉大膽執行了:1
劉大膽執行了:2
劉大膽執行了:3
劉大膽執行了:4
4.2 守護線程

定義自定義線程1

public class Day15Thread1 extends Thread {@Overridepublic void run(){for (int i = 0; i < 10; i++) {System.out.println(getName()+"執行了:"+i);}}}

定義自定義線程2

public class Day15Thread2  extends Thread{@Overridepublic void run(){for (int i = 0; i <100; i++) {System.out.println(getName()+"執行了:"+i);}}
}

定義主線程

public class Day15ThreadText {public static void main(String[] args) throws InterruptedException {Day15Thread1 t1 = new Day15Thread1();//t.run();t1.setName("劉大膽");Day15Thread2 t2 = new Day15Thread2();t2.setName("超級劉大膽");//設置t2為守護線程t2.setDaemon( true);t1.start();//開啟線程,jvm自動執行run方法t2.start();}
}
輸出結果:超級劉大膽執行了:0
劉大膽執行了:0
超級劉大膽執行了:1
劉大膽執行了:1
超級劉大膽執行了:2
劉大膽執行了:2
超級劉大膽執行了:3
劉大膽執行了:3
超級劉大膽執行了:4
劉大膽執行了:4
超級劉大膽執行了:5
劉大膽執行了:5
超級劉大膽執行了:6
劉大膽執行了:6
超級劉大膽執行了:7
劉大膽執行了:7
超級劉大膽執行了:8
劉大膽執行了:8
超級劉大膽執行了:9
劉大膽執行了:9
超級劉大膽執行了:10
超級劉大膽執行了:11
超級劉大膽執行了:12
超級劉大膽執行了:13
超級劉大膽執行了:14
超級劉大膽執行了:15
超級劉大膽執行了:16
超級劉大膽執行了:17
超級劉大膽執行了:18
超級劉大膽執行了:19

由此可見 當非守護線程結束后 守護線程不會直接結束 而是執行一段時間后再結束

4.3 禮讓線程
場景說明:如果兩個線程一起執行,可能會執行一會兒線程A,再執行一會線程B,或者可能線程A執行完畢了,線程B再執行那么我們能不能讓兩個線程盡可能的平衡一點 -> 盡量讓兩個線程交替執行
注意:只是盡可能的平衡,不是絕對的你來我往,有可能線程A線程執行,然后禮讓了,但是回頭A又搶到CPU使用權了   
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i<= 10; i++) {System.out.println(Thread.currentThread().getName()+"...執行了"+i);//禮讓線程Thread.yield();}}
}
public class Demo01Thread {public static void main(String[] args){MyThread t1 = new MyThread();//設置線程名字t1.setName("趙四");MyThread t2 = new MyThread();t2.setName("廣坤");t1.start();//開啟線程,jvm自動執行run方法t2.start();//開啟線程,jvm自動執行run方法}
}
4.4 插入線程
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i <= 10; i++) {System.out.println(Thread.currentThread().getName()+"...執行了"+i);}}
}
public class Demo01Thread {public static void main(String[] args) throws InterruptedException {MyThread t = new MyThread();//設置線程名字t.setName("趙四");t.start();//開啟線程,jvm自動執行run方法/*join(),插入線程將t1插入到當前線程前面,現在只有兩條線程,一個t1,一個主線程我們想將t1插入到主線程前面,主線程就是當前線程*/t.join();for (int i = 0; i <= 10; i++) {System.out.println(Thread.currentThread().getName()+"...執行了"+i);}}
}

5、第二種方式_實現Runnable接口

1.創建一個自定義類,實現Runnable接口
2.重寫run方法,設置線程任務
3.創建自定義類對象
4.利用Thread類的構造,創建Thread對象 ->Thread(Runnable r)Thread t1 = new Thread(自定義類對象)
5.調用start方法,開啟線程    

定義實現類

public class Day15Runnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+"執行了:"+i);}}
}

定義主線程

public class DayRunnableThread {public static void main(String[] args) {Day15Runnable t1 = new Day15Runnable();/*Thread(Runnable r)Thread(Runnable r,String name) -&gt; 創建Thread對象的同時給線程設置名字*/Thread t = new Thread(t1);t.start();for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+"執行了:"+i);}}
}

6、兩種實現多線程的方式區別

1.繼承Thread: 由于繼承只能進行單繼承,會有很大的局限性

2.實現Runnable: 可以繼承一個父類的同時實現一個或者多個接口,沒有繼承的局限性

7、匿名內部類創建多線程

package com.code.day15;public class Day15RunnableNI {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+"執行了:"+i);}}}).start();
//Thread(Runnable r ,String name)new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName()+"執行了:"+i);}}},"超級劉大膽").start();}
}

三、線程安全

1.線程不安全原因

出現線程不安全的原因:多個線程同時訪問同一個資源

線程不安全代碼

public class Day15Safe implements Runnable {int ticket = 100;@Overridepublic void run() {while (true) {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");ticket--;}}}
}
package com.code.day15;public class Day15SafeTest {public static void main(String[] args) {Day15Safe t = new Day15Safe();Thread t1 = new Thread(t,"趙四");Thread t2 = new Thread(t,"廣坤");Thread t3 = new Thread(t,"劉能");t1.start();t2.start();t3.start();}
}

2.解決線程安全問題的第一種方式(使用同步代碼塊)

1.格式:synchronized(鎖對象){可能出現線程不安全的代碼}2.鎖對象:a.任意對象b.想要實現線程安全,多個線程之間用的鎖對象就必須是同一個鎖對象3.線程執行,搶到鎖進入同步代碼塊執行,其他線程等待排隊,需要等著執行的線程出了同步代碼塊,將鎖釋放,其他等待線程才能搶鎖,進入到同步代碼塊中執行

定義自定義線程及鎖

package com.code.day15;public class Day15Safe implements Runnable {int ticket = 100;Object obj = new Object();@Overridepublic void run() {while (true) {try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (obj){if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");ticket--;}}}}
}

測試

package com.code.day15;public class Day15SafeTest {public static void main(String[] args) {Day15Safe t = new Day15Safe();Thread t1 = new Thread(t,"趙四");Thread t2 = new Thread(t,"廣坤");Thread t3 = new Thread(t,"劉能");t1.start();t2.start();t3.start();}
}
輸出結果:
趙四...買了第100張票
劉能...買了第99張票
廣坤...買了第98張票
劉能...買了第97張票
趙四...買了第96張票
廣坤...買了第95張票
廣坤...買了第94張票
劉能...買了第93張票
趙四...買了第92張票 ......

3.解決線程安全問題的第二種方式:同步方法

3.1普通同步方法_非靜態

定義鎖

package com.code.day15;public class Day15Safe implements Runnable {int ticket = 100;Object obj = new Object();@Overridepublic void run() {while (true) {try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}method();}}public synchronized void method() {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");ticket--;}//三個對象共享一個鎖
//    public void method() {
//        synchronized (this) { 
//            if (ticket > 0) {
//                System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");
//                ticket--;
//            }
//        }
//    }}
}
3.2靜態同步方法
1.格式:修飾符 static synchronized 返回值類型 方法名(形參){方法體return 結果}2.默認鎖:當前類.class -> class對象,現在先記住,class對象在 反射會講
package com.code.day15;public class Day15Safe implements Runnable {static int ticket = 100;Object obj = new Object();@Overridepublic void run() {while (true) {try {Thread.sleep(1000L);} catch (InterruptedException e) {throw new RuntimeException(e);}method();}}public static synchronized void method() {if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");ticket--;
//        }
//    public void method() {
//        synchronized (Day15SafeTest.class) {
//            if (ticket > 0) {
//                System.out.println(Thread.currentThread().getName() + "...買了第" + ticket + "張票");
//                ticket--;
//            }}}
}

兩種方法的差距主要在成員變量與方法的static

關于StringBuilder線程不安全與StringBuffer線程安全是因為StringBuffer中放置了安全鎖

四、死鎖

1、死鎖介紹(鎖嵌套就可能導致死鎖)

指的是兩個或者兩個以上的線程在執行的過程中由于競爭同步鎖而產生的一種阻塞現象;如果沒有外力的作用,他們將無法繼續執行下去,這種情況稱之為死鎖
根據上圖所示:線程1正在持有鎖1,但是線程1必須再拿到鎖2,才能繼續執行
而線程2正在持有鎖2,但是線程2需要再拿到鎖1,才能繼續執行
此時兩個線程處于互相等待的狀態,就是死鎖,在程序中的死鎖將出現在同步代碼塊的嵌套中

2、死鎖的分析

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

3、代碼實現

定義鎖A

public class LockA {public static LockA lockA = new LockA();
}

定義鎖B

public class LockB {public static LockB lockB = new LockB();
}

自定義線程

package com.code.day15;public class DieLock implements Runnable {private boolean flag;public DieLock(boolean flag) {this.flag = flag;}@Overridepublic void run() {if (flag) {synchronized (LockA.lockA) {System.out.println("A線程開始執行");synchronized (LockB.lockB) {System.out.println("B線程開始執行");}}} else {synchronized (LockB.lockB) {System.out.println("B線程開始執行");synchronized (LockA.lockA) {System.out.println("A線程開始執行");}}}}
}

測試

package com.code.day15;public class Day15LockTest {public static void main(String[] args) {DieLock dieLock1 = new DieLock( true);DieLock dieLock2 = new DieLock( false);Thread thread1 = new Thread(dieLock1);Thread thread2 = new Thread(dieLock2);thread1.start();thread2.start();}
}

A線程開始執行
B線程開始執行

由此可見以上程序出現了死鎖問題
em.out.println(“B線程開始執行”);
}
}
} else {
synchronized (LockB.lockB) {
System.out.println(“B線程開始執行”);
synchronized (LockA.lockA) {
System.out.println(“A線程開始執行”);
}
}
}
}
}


**測試**```java
package com.code.day15;public class Day15LockTest {public static void main(String[] args) {DieLock dieLock1 = new DieLock( true);DieLock dieLock2 = new DieLock( false);Thread thread1 = new Thread(dieLock1);Thread thread2 = new Thread(dieLock2);thread1.start();thread2.start();}
}

A線程開始執行
B線程開始執行

由此可見以上程序出現了死鎖問題

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98003.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98003.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98003.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

產品月報|睿本云8月產品功能迭代

睿本云8月更新已陸續上線&#xff01; 睿本云8月產品月報&#xff0c;點擊查收&#x1f447;小程序支付成功彈窗廣告、企業會員增加卡券銷售和卡券退貨模塊、工廠端可批量新增多門店訂貨單、門店端和工廠端新增“極速訂貨”、商品調撥業務支持自定義多種流程配置等功能迭代更新…

融云:當我們談論 AI 重構業務時,我們到底在談論什么

所有業務都值得用 AI 重新做一次。 這句話正在從一句鼓舞人心的口號&#xff0c;演變為一場無人可避的商業現實。AI 帶來的結構性機會&#xff0c;意味著企業有機會從根本上重構成本、效率與體驗的曲線。但這一切最終都要回到一個無比務實的問題上&#xff1a; AI 究竟如何在我…

org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1異常

org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length 1異常問題解決一、問題背景二、錯誤現象三、原因分析核心問題&#xff1a;字符集不匹配四、解決過程試錯路徑記錄五、最終方案1.創建launch.json文件&#xff0c;修改VSCode…

【C語言】深入理解指針(5)

目錄 sizeof和strlen 1.sizeof 2.strlen 3. sizeof 和 strlen 的對比 sizeof和strlen 1.sizeof sizeo正名&#xff1a;sizeof是操作符&#xff0c;不是函數&#xff0c;sizeof是操作符&#xff0c;括號內如果有計算不會進行計算sizeof 是操作符&#xff0c;用于計算變量所…

動態代理設計模式

JDK動態代理實現 動態代理利用了JDK API,動態地在內存中構建代理對象,從而實現對目標對象的代理功能.動態代理又被稱為JDK代理或接口代理. 靜態代理與動態代理的區別: 靜態代理在編譯時就已經實現了,編譯完成后代理類是一個實際的class文 動態代理是在運行時動態生成的,即編譯…

《Html泛型魔法學院:用霍格沃茨風格網頁教授集合框架》

一、項目概述 這個創意教學網頁&#xff0c;將Java泛型與集合框架知識融入霍格沃茨魔法世界主題。通過沉浸式UI設計和交互式代碼練習&#xff0c;讓抽象的技術概念變得生動有趣。主要技術棧包括&#xff1a; HTML5語義化結構Tailwind CSS框架Font Awesome圖標庫純JavaScript交…

學習PaddlePaddle--環境配置-PyCharm + Conda?

第一階段&#xff1a;安裝與配置 Python 和 Conda?? 雖然 PyCharm 可以管理環境&#xff0c;但我們先獨立準備好 Conda 環境&#xff0c;這樣更清晰可靠。 ??1. 安裝 Miniconda (Python 環境管理)?? 1. ??下載??&#xff1a; ? 訪問 Miniconda 官網。 ? 選擇 ??M…

【數據庫】Sql Server數據庫中isnull、iif、case when三種方式的使用和空值判斷

大家好&#xff0c;我是全棧小5&#xff0c;歡迎來到《小5講堂》。 這是《Sql Server》系列文章&#xff0c;每篇文章將以博主理解的角度展開講解。 溫馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不對之處望指正&#xff01; 目錄前言ISNULL用法c…

【藍橋杯選拔賽真題64】C++最大空白區 第十四屆藍橋杯青少年創意編程大賽 算法思維 C++編程選拔賽真題解

C++最大空白區 第十四屆藍橋杯青少年創意編程大賽C++選拔賽真題 博主推薦 所有考級比賽學習相關資料合集【推薦收藏】 1、C++專欄 電子學會C++一級歷年真題解析 電子學會C++二級歷年真題解析

試用Augment編寫python腳本實現智能家居3D環境交互響應

環境配置 VS Code中直接安裝Augment擴展&#xff0c;然后郵箱登錄就能獲得7天的試用。 從如下位置安裝3D建模軟件Blender&#xff1a; https://www.blendercn.org/downloadme#xiazai Blender 是一款免費開源的 3D 創作套件。它支持整個三維流程&#xff1a;建模、綁定、動畫…

【架構師干貨】系統架構設計

1. 軟件架構概述 從需求分析到軟件設計之間的過渡過程稱為軟件架構。只要軟件架構設計好了&#xff0c;整個軟件就不會出現坍塌性的錯誤&#xff0c;即不會崩潰。 架構設計就是需求分配&#xff0c;將滿足需求的職責分配到組件上。 軟件架構為軟件系統提供了一個結構、行為和屬…

Java設計模式之結構型—享元模式

Java中最常用的設計模式-CSDN博客 把“不可變且可復用”的細粒度對象緩存起來&#xff0c;用“共享”代替“新建”&#xff0c;從而節省內存。 經典場景 字符串常量池、Integer.valueOf(-128~127)、Android Message.obtain() 游戲粒子、編輯器字形、地圖瓦片、線程池中的任務…

cursor+python輕松實現電腦監控

小伙伴們&#xff0c;今天我們利用cursor不寫一行代碼開發一個電腦的系統狀態監控小應用&#xff01;下載安裝cursor&#xff1a;網址&#xff1a;https://www.cursor.com/cn下載后雙擊安裝輸入提示詞&#xff1a; 制作一個winswos應用&#xff0c;實現顯示時間精確到秒&…

信號調制與解調 matlab仿真

信號調制與解調 matlab仿真 原始信號--頻譜為cos(Wt*w)函數&#xff0c;外形如饅頭調制解調傅里葉變換測試FT的頻譜是否為鋸齒波理想低通濾波器,截至頻率Wm傅里葉變換頻譜為鋸齒波函數的時域信號函數傅里葉變換調制頻率1理想低通濾波調制頻率2理想低通濾波 % 調制定理演示Dem…

IIS服務器下做瀏覽器緩存

你的這個問題問得非常好&#xff0c;很多開發者在同時使用重寫和緩存時都會遇到。簡單來說&#xff1a;你添加的 <staticContent> 和 <clientCache> 配置本身不會影響或干擾 重寫規則的工作。它們各司其職&#xff0c;在 IIS 處理請求的不同階段發揮作用。 但是&a…

Flutter 3.35.2 以上版本中 數字轉字符串的方法指南

在 Flutter 3.35.2 (對應 Dart 2.19 及以上版本) 中&#xff0c;將數字轉換為字符串主要依賴于 Dart 語言本身提供的原生方法。這些方法穩定且向后兼容。下面我為你介紹幾種主要的方法和案例。 &#x1f522; 數字轉字符串的基本方法方法名適用類型描述常用場景toString()int, …

C#基礎(⑤ProcessStartInfo類和Process類)

1. 它是什么&#xff1f;ProcessStartInfo 是 C# 里的一個類&#xff08;屬于 System.Diagnostics 命名空間&#xff09;&#xff0c;作用是&#xff1a;定義要啟動的程序路徑&#xff08;比如 notepad.exe&#xff09;設置啟動時的參數&#xff08;比如打開哪個文件&#xff0…

《設計模式之禪》筆記摘錄 - 19.備忘錄模式

備忘錄模式的定義備忘錄模式(Memento Pattern)提供了一種彌補真實世界缺陷的方法&#xff0c;讓“后悔藥”在程界序的世界中真實可行&#xff0c;其定義如下&#xff1a;Without violating encapsulation, capture and externalize an objects internal state so that the obje…

22、Jenkins容器化部署Java應用

22、Jenkins容器化部署Java應用 1、準備Dockerfile 將Dockerfile文件放入項目目錄下 FROM registry.cn-hangzhou.aliyuncs.com/xx_blog/openjdk:21-jdk LABEL maintainer"xxqq.com" #復制打好的jar包 COPY target/*.jar /app.jar RUN apk add -U tzdata; \ ln -sf /…

基于單片機智能水龍頭/智能洗漱臺設計

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 該設計采用單片機作為核心控制器&#xff0c;結合紅外傳感器、水流傳感器和電磁閥等模塊&#xf…