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();}}