• 暑期JAVA学习(31)多线程的创建


    一、什么是线程

    ●线程(thread)是一个程序内部的一条执行路径。
    ●启动程序执行后,main方法的执行其实就是一条单独的执行路径。
    ●程序中如果只有一条执行路径,那么这个程序就是单线程的程序。

    二、什么是多线程

    ●多线程是指从软硬件上实现多条执行流程的技术。

    三、Thread类

    ●Java是通过java.lang.Thread 类来代表线程的。
    ●按照面向对象的思想,Thread类应该提供了实现多线程的方式。

    四、多线程的实现方案一:继承Thread类

    (1)步骤

    ①定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法

    ②创建MyThread类的对象

    ③调用线程对象的start()方法启动线程(启动后还是执行run方法的)

    (2)具体实现

    在这里插入图片描述
    在这里插入图片描述

    /**
     *目标:多线程的创建方式一:继承Thread类实现。
     */
    public class ThreadDemo01 {
        public static void main(String[] args) {
            //3、new一个新线程对象
            Thread t = new MyThread();//多态
            //4、调用start方法启动线程(执行的还是run方法)
            t.start();
    
            for (int i = 0; i < 5; i++) {
                System.out.println("主线程执行输出:" + i);
            }
    
        }
    }
    
    /**
     *1、定义一个线程类继承Thread类
     */
    class MyThread extends Thread{
        /**
         * 2、重写run方法,里面是定义线程要干什么
         */
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程执行输出:" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (3)优缺点

    ●优点:编码简单
    ●缺点:线程类已经继承Thread,无法继承其他类,不利于扩展

    (4)总结

    ①为什么不直接调用run方法,而是调用start启动线程?

    ●直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
    ●只有调用start方法才是启动一个新的线程执行。

    ②为什么建议不要把主线程任务放在子线程之前呢?

    ●这样主线程一直是先跑完的,相当于是一个单线程的效果了。
    在这里插入图片描述
    在这里插入图片描述

    ③方式一是如何实现多线程的?

    ①继承Thread类
    ②重写run方法
    ③创建线程对象
    ④调用start()方法启动。

    五、多线程的实现方案二:实现Runnable接口

    (1)步骤

    ①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

    ②创建MyRunnable任务对象

    ③把MyRunnable任务对象交给Thread处理。

    ④调用线程对象的start()方法启动线程

    (2)Thread的构造器

    构造器说明
    public Thread(String name)可以为当前线程指定名称
    public Thread(Runnable target)封装Runnable对象成为线程对象
    public Thread(Runnable target ,String name )封装Runnable对象成为线程对象,并指定线程名称

    (3)具体实现

    在这里插入图片描述
    在这里插入图片描述

    /**
     * 目标:学会线程的创建方式二,理解它的优缺点。
     */
    public class ThreadDemo02 {
        public static void main(String[] args) {
            // 3、创建一个任务对象
            Runnable target = new MyRunnable();//多态
            // 4、把任务对象交给Thread处理
            Thread t = new Thread(target);
            // 5、启动线程
            t.start();
    
            for (int i = 0; i < 5; i++) {
                System.out.println("主线程执行输出:" + i);
            }
        }
    }
    
    /**
     * 1、定义一个线程任务类 实现Runnable接口
     */
    class MyRunnable implements Runnable{
        /**
         * 2、重写run方法,定义线程的执行任务
         */
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程执行输出:" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (4)优缺点

    ●优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
    ●缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。

    六、多线程的实现方案二:实现Runnable接口(匿名内部类形式)

    (1)步骤

    ①可以创建Runnable的匿名内部类对象。

    ②交给Thread处理。

    ③调用线程对象的start()启动线程。

    (2)具体实现

    在这里插入图片描述
    在这里插入图片描述

    /**
     目标:学会线程的创建方式二(匿名内部类方式实现,语法形式)
     */
    public class ThreadDemoOther {
        public static void main(String[] args) {
            //接口可以实现匿名内部类
            Runnable target = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        System.out.println("子线程1执行输出:" + i);
                    }
                }
            };
            Thread t = new Thread(target);
            t.start();
    
            //简化
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        System.out.println("子线程2执行输出:" + i);
                    }
                }
            }).start();
    
            //再一次简化
            new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程3执行输出:" + i);
                }
            }).start();
    
            for (int i = 0; i < 5; i++) {
                System.out.println("主线程执行输出:" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    七、多线程的实现方案三:利用Callable、FutureTask接口实现

    (1)步骤

    1、得到任务对象:
    ①定义类实现Callable接口,重写call方法,封装要做的事情
    ②用FutureTask把Callable对象封装成线程任务对象。
    2、把线程任务对象交给Thread处理。
    3、调用Thread的start方法启动线程,执行任务
    4、线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果。

    (2)FutureTask的API

    方法名称说明
    public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象
    public V get() throws Exception获取线程执行call方法返回的结果

    (3)具体实现

    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

    /**
     * 目标:学会线程的创建方式三:实现Callable接口,结合FutureTask完成。
     */
    public class ThreadDemo03 {
        public static void main(String[] args) {
            // 3、创建Callable任务对象
            Callable call1 = new MyCallable(100);
            // 4、把Callable任务对象 交给 FutureTask 对象
            //  FutureTask对象的作用1: 是Runnable的对象(实现了Runnable接口),可以交给Thread了
            //  FutureTask对象的作用2: 可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果
            FutureTask f1 = new FutureTask<>(call1);
            // 5、交给线程处理
            Thread t1 = new Thread(f1);
            // 6、启动线程
            t1.start();
    
            Callable call2 = new MyCallable(200);
            FutureTask f2 = new FutureTask<>(call2);
            Thread t2 = new Thread(f2);
            t2.start();
    
            try {
                // 如果f1任务没有执行完毕,这里的代码会等待,直到线程1跑完才提取结果。
                String rs1 = f1.get();
                System.out.println("第一个结果:" + rs1);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            try {
                // 如果f2任务没有执行完毕,这里的代码会等待,直到线程2跑完才提取结果。
                String rs2 = f2.get();
                System.out.println("第二个结果:" + rs2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     *  1、定义一个任务类 实现Callable接口  应该申明线程任务执行完毕后的结果的数据类型
     */
    class MyCallable implements Callable{
        private int n;
        public MyCallable(int n) {
            this.n = n;
        }
    
        /**
         * 2、重写call方法(任务方法)
         * @return
         * @throws Exception
         */
        @Override
        public String call() throws Exception {
            int sum = 0;
            for (int i = 0; i <= n; i++) {
                sum += i;
            }
            return "子线程执行的结果是:" + sum;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    (4)优缺点

    ●优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强,并且可以在线程执行完毕后去获取线程执行的结果。
    ●缺点:编码复杂一点。

    八、三种方式对比

    方式优点缺点
    继承Thread类编程比较简单,可以直接使用Thread类中的方法扩展性较差,不能再继承其他的类,不能返回线程执行的结果
    实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能返回线程执行的结果
    实现Callable接口扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果编程相对复杂
  • 相关阅读:
    机器学习中的偏差漂移:挑战与缓解
    滑动窗口延申题:最小覆盖子串
    MyBatis
    USACO 2021 December Contest, Silver
    VMLogin配置高手进阶
    大量数据同步
    Spring Boot 整合 NoSQL(3)
    设计模式- 代理模式(Proxy Pattern)结构|原理|优缺点|场景|示例
    多线程相关问题
    java-php-python-ssm糖果销售管理系统计算机毕业设计
  • 原文地址:https://blog.csdn.net/xjl243636988/article/details/125972973