02 線程創建
Thread , Runnable , Callable
三種創建方式
Thread class - 繼承Thread類 (重點)
Runnable接口 - 實現Runnable接口 (重點)
Callable接口 - 實現Callable接口 (了解)
Thread 類實現
它繼承了老祖宗 Object
java.lang.Object
java.lang.Thread
它實現了 Runnable接口
線程是程序中執行的線程. Java虛擬機允許應用程序同時執行多個執行線程.
每個線程都有優先權. 就是你的優先權更高你先執行, 你的優先權低你就后執行, 還有守護線程, 和用戶線程, 這個地方先不聊, 本章主要講如何創建線程
創建一個新的線程有兩種方法, 一個是將一個類聲明為Thread的子類, 這個子類應該重新run類的方法Thread. 然后可以分配并啟動子類的實例. 例如, 計算大于規定值的素數的線程可以寫成如下:
- 自定義線程類繼承**Thread類**
- 重寫**run()**方法
- 創建線程對象, 調用**start()**方法啟動線程
繼承Thread類實現
我們下面用代碼實現一下:
package com.jean.thread;//創建線程方式一: 繼承Thread類, 重寫run()方法, 調用start開啟線程
public class TestThread1 extends Thread {// 繼承完, 立即重寫run方法@Overridepublic void run() {
// run方法線程體for (int i = 0; i < 20; i++) {System.out.println("我在看代碼----"+i);}}public static void main(String[] args) {
// main線程, 主線程for (int i = 0; i < 20; i++) {System.out.println("我在學習多線程----"+i);}}
}
我們執行后, 控制臺加載完后就一瞬間輸出了20個我在學習多線程, 我們如果想把另一個線程開啟怎么開呢?
package com.jean.thread;//創建線程方式一: 繼承Thread類, 重寫run()方法, 調用start開啟線程
public class TestThread1 extends Thread {// 繼承完, 立即重寫run方法@Overridepublic void run() {
// run方法線程體for (int i = 0; i < 20; i++) {System.out.println("我在看代碼----"+i);}}public static void main(String[] args) {
// main線程, 主線程// 首先創建它的一個對象TestThread1 testThread1 = new TestThread1();
// 調用start方法, 開啟線程testThread1.start();for (int i = 0; i < 20; i++) {System.out.println("我在學習多線程----"+i);}}
}
我們調用了start方法后, 控制臺明顯執行的先后順序就隨機了, 所以說
調用start()方法是同時來運行的, 交替執行
我們的多線程調用了一個start方法, 它直接走下來進了start方法, 他開辟了一條新的線程, 它去執行它的方法, 主線程依據去走主線程的
然后我們再改調用run()方法
package com.jean.thread;//創建線程方式一: 繼承Thread類, 重寫run()方法, 調用start開啟線程
public class TestThread1 extends Thread {// 繼承完, 立即重寫run方法@Overridepublic void run() {
// run方法線程體for (int i = 0; i < 20; i++) {System.out.println("我在看代碼----"+i);}}public static void main(String[] args) {
// main線程, 主線程// 首先創建它的一個對象TestThread1 testThread1 = new TestThread1();
// 調用run方法, 開啟線程testThread1.run();for (int i = 0; i < 20; i++) {System.out.println("我在學習多線程----"+i);}}
}
使用run方法調用, 他先走run方法, 執行完了才去執行主路徑
總結:
線程開啟不一定立即執行, 由CPU調度安排
多線程網圖下載
案例: 下載圖片
使用多線程同時去下載幾個圖片
- 先引入一下jar包 Commons IO包.
可以直接去百度搜索Commons IO , 是Apache下的.
-
Commons IO是針對開發IO流功能的工具類庫.
-
FileUtils文件工具, 復制url到文件
Commons-io包的下載地址
點擊圖中紅色圈起來的jar鏈接即可實現下載
創建lib文件, 把lib目錄添加為庫.
- 創建lib文件
- 點擊lib文件獲取焦點, 右鍵點擊
- 選擇添加為庫
- 添加為jar
添加成功之后, 文件前會有一個箭頭
新建TestDownload文件
package com.jean.thread;import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;
import java.net.URL;/*** @BelongsProject: Thread-class01* @BelongsPackage: com.jean.thread* @Author: Jean_z* @CreateTime: 2024-05-13 08:31* @Description: TODO: 練習Thread, 實現多線程同步下載圖片* @Version: 1.0*/
public class TestDownload implements Runnable{private String url; //網絡圖片地址private String name; //保存的文件名// 構造器public TestDownload(String url, String name) {this.url = url;this.name = name;}// 下載圖片線程的執行體@Overridepublic void run() {WebDownload webDownload = new WebDownload();webDownload.download(url,name);System.out.println("下載了文件名為:" + name);}// 啟動線程public static void main(String[] args) {TestDownload testDownload1 = new TestDownload("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字1");TestDownload testDownload2 = new TestDownload("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字2");TestDownload testDownload3 = new TestDownload("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字3");// Thread類方法
// testDownload1.start();
// testDownload2.start();
// testDownload3.start();// Runnable接口方法new Thread(testDownload1).start();new Thread(testDownload2).start();new Thread(testDownload3).start();}
}// 下載器
class WebDownload {
// 下載方法public void download(String url,String name) {
// FileUtils: 文件工具
// copyURLToFile 拷貝url地址到一個文件try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO異常, download 方法出問題了");}}
}
Runnable接口實現多線程.
跟推薦的一種實現多線程的方式: Runnable
創建線程方式2
- 實現Runnable接口,
- 重寫run方法, 執行線程需要丟入runnable接口實現類,調用start方法.
package com.jean.thread;/*** @BelongsProject: Thread-class01* @BelongsPackage: com.jean.thread* @Author: Jean_z* @CreateTime: 2024-05-13 09:38* @Description: TODO* @Version: 1.0*///創建線程方式2 : 實現Runnable接口, 重寫run方法, 執行線程需要丟入runnable接口實現類,調用start方法.
public class TestRunnable implements Runnable{// 繼承完, 立即重寫run方法@Overridepublic void run() {
// run方法線程體for (int i = 0; i < 20; i++) {System.out.println("我在看代碼----"+i);}}public static void main(String[] args) {
// main線程, 主線程
// 創建runnable接口的實現對象TestRunnable runnable = new TestRunnable();// 創建線程對象, 通過線程對象來開啟我們的線程, 代理
// Thread thread = new Thread(runnable);
// 調用start方法, 開啟線程new Thread(runnable).start();for (int i = 0; i < 2000; i++) {System.out.println("我在學習多線程----"+i);}}
}
Callable 方式 實現多線程
第三種實現多線程的方式: Callable
我們基于多線程下載網絡圖片代碼, 修改.
-
實現Callable接口
-
重寫call方法 類型
-
創建執行事務
ExecutorService executorService = Executors.newFixedThreadPool (3);
-
提交執行
-
獲取執行結果, boolean類型
-
關閉服務
之前是重寫run方法, 我們這里不一樣, 重寫的是call方法, 注意方法類型是布爾.
第三種方式, 了解即可 !
package com.jean.thread;import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;/*** @BelongsProject: Thread-class01* @BelongsPackage: com.jean.thread* @Author: Jean_z* @CreateTime: 2024-05-13 08:31* @Description: TODO: 練習Thread, 實現多線程同步下載圖片* @Version: 1.0*/
public class TestCallable implements Callable<Boolean> {private String url; //網絡圖片地址private String name; //保存的文件名// 構造器public TestCallable(String url, String name) {this.url = url;this.name = name;}// 下載圖片線程的執行體@Overridepublic Boolean call() {WebDownload2 webDownload = new WebDownload2();webDownload.download(url,name);System.out.println("下載了文件名為:" + name);return true;}// 啟動線程public static void main(String[] args) throws ExecutionException, InterruptedException {TestCallable testCallable1= new TestCallable("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字1");TestCallable testCallable2= new TestCallable("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字2");TestCallable testCallable3= new TestCallable("https://img-home.csdnimg.cn/images/20240511083237.png", "我是圖片的名字3");// Thread類方法
// testDownload1.start();
// testDownload2.start();
// testDownload3.start();// 創建執行事務ExecutorService executorService = Executors.newFixedThreadPool(3);// 提交執行Future<Boolean> r1 = executorService.submit(testCallable1);Future<Boolean> r2 = executorService.submit(testCallable2);Future<Boolean> r3 = executorService.submit(testCallable3);// 獲取結果boolean rs1 = r1.get();boolean rs2 = r2.get();boolean rs3 = r3.get();System.out.println(rs1);System.out.println(rs2);System.out.println(rs3);// 關閉服務executorService.shutdownNow();}
}// 下載器
class WebDownload2 {
// 下載方法public void download(String url,String name) {
// FileUtils: 文件工具
// copyURLToFile 拷貝url地址到一個文件try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO異常, download 方法出問題了");}}
}
多線程 “龜🐢” “兔🐇” 賽跑案例
案例需求:
- 首先先來個賽道距離, 然后要離重點越來越近
- 判斷比賽是否結束
- 打印出勝利者
- 龜兔賽跑開始
- 故事中是烏龜🐢速度慢但是依舊是烏龜贏的, 兔子🐇需要睡覺, 所以我們來模擬兔子睡覺💤
- 終于, 烏龜🐢贏得比賽.
sleep多線程的延時方法
Thread.sleep ( 5000 ) // 這里是毫秒
package com.jean.thread;/*** @BelongsProject: Thread-class01* @BelongsPackage: com.jean.thread* @Author: Jean_z* @CreateTime: 2024-05-13 10:21* @Description: TODO* @Version: 1.0*/
//模擬龜兔賽跑
public class TestRace implements Runnable{// 勝利者private static String winner;
// private static String winner;@Overridepublic void run() {for (int i = 1; i <= 100; i++) {// 模擬兔子休息if (Thread.currentThread().getName().equals("兔子🐇")) {try {Thread.sleep(30);} catch (InterruptedException e) {e.printStackTrace();System.out.println("兔子🐇喝伏特加了, 無比清醒, 不想睡覺");}}// 模擬烏龜速度if (Thread.currentThread().getName().equals("烏龜🐢")) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}// 判斷比賽是否接結束boolean flag = gameOver(i);
// 比賽結束停止程序if (flag) {break;}System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");}}// 判斷是否完成比賽private boolean gameOver(int steps) {
// 判斷是否有勝利者if (winner!=null) { //已經存在勝利者了return true;}{if (steps>=100){winner = Thread.currentThread().getName();System.out.println("最終勝利者 is "+winner);return true;}}return false;}//賽道public static void main(String[] args) {TestRace race = new TestRace();new Thread(race,"兔子🐇").start();new Thread(race,"烏龜🐢").start();}}