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方法
    @Override
    public 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方法
    @Override
    public 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方法
    @Override
    public 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;
    }
//    下载图片线程的执行体
    @Override
    public 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方法
    @Override
    public 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;
    }
//    下载图片线程的执行体
    @Override
    public 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;
    @Override
    public 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();
    }
}



















