【多线程】创建线程到底是多少种方法?

news2024/11/13 11:22:44

🥰🥰🥰来都来了,不妨点个关注叭!
👉博客主页:欢迎各位大佬!👈

在这里插入图片描述

文章目录

  • 1. 创建线程的两种方式总结(最官方)
    • 1.1 继承 Thread 类
    • 1.2 实现 Runnable 接口
    • 1.3 优先考虑使用第二种 —— 实现 Runnable 接口
  • 2. 创建线程的其它方式总结
    • 2.1 创建线程的五种方式总结一
    • 2.2 创建线程的五种方式总结二
      • 2.2.1 线程池创建线程
      • 2.2.2 实现 Callable 创建线程
      • 2.2.3 定时器工具类创建线程

如标题所示,创建线程到底有几种方法?

答案是:不是固定的,可以说有两种,可以说有三种,可以说有四种,可以说有五种…
只要自圆其说即可,可不是瞎说哦 ~

同时,我们可以了解到,在计算机这个奇妙的世界里,很多问题答案并不是固定的,只要我们掌握里面的核心与精髓即可,尤其是像这种问题,根据学习者自己总结的类型,答案肯定是不固定的,每个人的理解不同,归纳的方式不同,所以,理解透彻,掌握核心,才是我们需要做的呀!

下面我们一起看看吧~

1. 创建线程的两种方式总结(最官方)

  • 方式一:继承 Thread 类
  • 方式二:实现 Runnable 接口

通过官方文档 oracle,可以看到,官方给的解释,创建线程的方式是两种,查看方式如下:

在这里插入图片描述
在这里插入图片描述
即为创建新的执行线程有两种方法,一种是将一个类声明为 Thread 的子类,该子类应重写 Thread 类的 run 方法,另一种方法是声明一个实现 Runnable 接口的类,该类实现 run 方法

1.1 继承 Thread 类

class MyThread extends Thread {
    @Override
    public void run() {
        while(true) {
            System.out.println("这是继承Thread创建线程");
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
    }
}

1.2 实现 Runnable 接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while(true){
            System.out.println("这是实现Runnable接口创建线程");
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.start();
    }
}

其实本质上来说,创建线程的方式只有一种,就是通过 Thread 类实现,通过上述代码可以直观感受到,只是 Thread 类和 Runnable 接口这两种创建线程方式是通过 run() 方法的位置不同进行区分的。下面的几种创建线程方式的总结,归根到底还是上述的两种,究其本质发现,底层源码仍然是 Thread 类和 Runnable 接口实现的!

1.3 优先考虑使用第二种 —— 实现 Runnable 接口

理由有以下三个方面:

  • 耦合度角度:实现Runnable接口创建线程, Thread线程与run方法耦合开;而继承Thread类创建线程,Thread线程与run方法耦合在一起,因此,优先考虑实现Runnable接口创建线程;
  • 资源角度:每出现一个任务的时候,继承Thread类创建线程,都需要手动创建一个线程,线程的创建和销毁都会消耗比较大的资源,而实现Runnable接口的方式创建线程,仅需实现Runnable接口,将实现类作为参数加入到Thread()中即可,线程Thread通过线程池创建与管理,这样就可以减少线程的创建和销毁,因此,优先考虑实现Runnable接口创建线程;
  • 继承角度:由继承的知识可以知道:Java支持单继承、多层继承、不同类继承同一类,但是不支持多继承即一个子类继承多个父类,因此,一个子类只能继承一个父类,继承Thread类创建线程,采用的是继承的方式,只能继承这一个类,可扩展性大大降低,并且,如果不同类继承同一个Thread类,可能出现父类的run方法被子类重写导致run方法被覆盖的情况,因此,优先考虑实现Runnable接口创建线程。

2. 创建线程的其它方式总结

在这期内容中,介绍了五种创建线程的方式,可回顾这期内容 3.1创建线程 ,Thread类及其基本用法

2.1 创建线程的五种方式总结一

可以看到这期内容介绍的五种创建线程方式如下:

  • 方式一:使用继承Thread,重写run的方式
  • 方式二:使用实现Runnable,重写run的方式
  • 方式三:继承Thread,使用匿名内部类的方式
  • 方式四:实现Runnable类,使用匿名内部类
  • 方式五:lambda表达式(最推荐使用,最简单最直观写法)

显然易见,方式一和二与上面的创建线程的两种方式总结一致,而方式三,其实本质上还是使用继承Thread类,方式四、五本质上还是使用实现Runnable接口,只是使用的形式上是匿名类!

2.2 创建线程的五种方式总结二

  • 方式一:继承Thread类
  • 方式二:实现Runnable接口
  • 方式三:基于lambda
  • 方式四:实现Callable
  • 方式五:线程池创建线程

为什么有人会把基于lambda表达式创建线程的方式进行总结,因为它是最常用的,最直观最简单的写法

这里对线程池创建线程实现Callable创建线程进行介绍:

2.2.1 线程池创建线程

在日常学习或者是开发中,经常通过线程池来创建线程,那么线程池创建线程的本质到底是什么呢?
介绍线程池的文章可回顾这期内容:线程池

ExecutorService pool =  Executors.newFixedThreadPool(10);

在这里插入图片描述
线程池创建线程的方式,本质上来说,还是使用的是实现Runnable接口

2.2.2 实现 Callable 创建线程

与其它方式创建线程不同,实现 Callable 创建线程最大的优点是:可以返回执行完毕后的结果

1)创建任务对象

  • 定义一个类,实现Callable接口,重写call方法,描述线程的任务以及返回后的数据
  • 把Callable类型的对象封装成FutureTask,即线程任务

2)把线程任务对象交给Thread对象
3)使用Thread对象,调用start()方法启动线程
4)线程执行完毕后,通过FutureTask对象get方法获取线程任务执行结果

代码如下:

import java.util.concurrent.Callable;

    //1.MyCallable类实现Callable接口
	public class MyCallable implements Callable<String> {
    	private int n;
    	public MyCallable(int n){
        this.n = n;
    }

    //2.重写call方法
    @Override
    public String call() throws Exception {
        //描述线程的任务以及返回后的数据,求1至n的和并返回结果
        int sum = 0;
        for (int i = 0; i <= n; i++) {
            sum += i;
        }
        return "线程求出的和为:" + sum;
    }
}
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;


public class ThreadDemo {
    public static void main(String[] args) {
        //1.是一个实现Runnable的对象
        //2.可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果
        //3.创建一个Callable的对象
        Callable<String> call = new MyCallable(10);
        //4.把Callable的对象封装成一个FutureTask对象(任务对象)
        FutureTask<String> f1 = new FutureTask<>(call);
        //5.把任务对象交给一个Thread对象
        Thread t1 = new Thread(f1);
        t1.start();
        //6.获取线程执行完毕后返回的结果
        //如果代码执行到这,上面的线程t1还没有执行完,就会暂停,等待上面线程执行完毕之后才会获取结果
        String ret = null;
        try {
            ret = f1.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(ret);
    }
}

打印的结果为:

在这里插入图片描述
也可以使用匿名内部类的方式,直接重写call方法,代码如下:

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //只是创建了任务,还没有执行
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for(int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };

        //还需要找个人来完成这个任务即线程
        //Thread 不能直接传callable 需要再包装一层

        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
        //此处的get就是获取到上述任务call方法返回值的结果
        //调用get与join类似会阻塞等待,并获取到值
    }
}

在这里插入图片描述
实现Callable创建线程的方式,本质上来说,还是使用的是实现Runnable接口

还有小伙伴总结,定时器工具类创建线程,下面进行简单介绍:

2.2.3 定时器工具类创建线程

还记得这个例子嘛!定时器同时定义 3 个任务,按照时间顺序依次执行,可回顾定时器这期内容:定时器

public class ThreadDemo30 {
    public static void main(String[] args) {
        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello1");
            }
        },1000);


        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello2");
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello3");
            }
        },3000);
        
    }
}

在这里插入图片描述
实现定时器工具类创建线程的方式,本质上来说,还是使用的是实现Runnable接口

💛💛💛本期内容回顾💛💛💛
在这里插入图片描述
✨✨✨本期内容到此结束啦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2086786.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附+源代码流程)

三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附源代码流程) 文章目录 三. Spring Boot 当中的“容器功能” 和 “配置绑定” 的详细剖析(附源代码流程)1. Spring Boot 是继续支持了 Spring 当中的注解的1.2 Spring 当中的 Component&#xff0c;Controller…

通过visual studio进行dump文件调试和分析

0、前言 很多时候程序crash之后需要分析原因。对于C/C程序&#xff0c;一般最常见的场景和方法就是根据dump文件进行分析。 1、分析的前提条件 进行dump文件分析&#xff0c;需要以下文件&#xff1a; 进程crash时产生的dump文件程序源码进程对应的程序exe文件编译exe文件时产…

QT Quick QML 添加海康威视SDK云台控制模块

文章目录 1. 配置海康威视 SDK 下载SDK文件移植工程文件添加 2. 函数调用流程接口参考代码 3. 代码后端核心代码前端核心代码 GitHub 源码: QmlLearningPro &#xff0c;选择子工程 HkwsDemo.pro &#xff08;暂未上传&#xff09; QML 其它文章请点击这里: QT QUICK …

Monibuca实战:如何用Go语言打造高效的直播后端

简介 Monibuca&#xff08;简称&#xff1a;m7s&#xff09; 是一个开源的实时流媒体服务器开发框架&#xff0c;使用 Go 语言编写。 它的设计目标是提供一个高性能、可扩展、易于定制的实时流媒体服务器解决方案。 Monibuca 的核心理念是模块化&#xff0c;允许开发者根据需…

文件禁止外发的方法有哪些?企业如何禁止文件外发:六个控制文件外发的小窍门!

想象一下&#xff0c;企业信息如同珍贵的宝藏&#xff0c;而文件外发就像不经意间打开的后门&#xff0c;让宝藏暴露在风雨之中&#xff01;今天&#xff0c;我们就来聊聊如何给这扇后门加上六道坚实的锁&#xff0c;确保企业信息的安全无虞。让我们一起探索六个控制文件外发的…

Mask R-CNN论文原理讲解

论文:arxiv.org/pdf/1703.06870 代码&#xff1a;maskrcnn-benchmark:Fast, modular reference implementation of Instance Segmentation and Object Detection algorithms in PyTorch. - GitCode Mask R-CNN简介 Mask R-CNN是何凯明大神的新作。Mask R-CNN是一种在有效检测…

武器弹药制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

武器弹药制造领域作为国防工业的重要组成部分&#xff0c;其数字化转型更是关乎国家安全与军事实力提升的关键。随着5G、物联网、大数据、云计算及人工智能等先进技术的融合应用&#xff0c;武器弹药制造5G智能工厂物联数字孪生平台应运而生&#xff0c;正逐步成为推进制造业数…

分享5款支持论文写作网站先稿后付的网站!

在当今学术研究和学术写作领域&#xff0c;AI论文写作工具已经成为不可或缺的助手。这些工具不仅能够提高写作效率&#xff0c;还能帮助研究人员生成高质量的论文内容。特别是那些提供“先稿后付”服务模式的网站&#xff0c;更是为用户提供了极大的便利和保障。以下是五款值得…

记录|SPC公式小结

目录 前言一、基本缩写二、Xbar和R的控制线三、相关系数表更新时间 前言 参考文章&#xff1a; 参考视频&#xff1a; SPC公式小结 一、基本缩写 二、Xbar和R的控制线 三、相关系数表 更新时间 2024.08.29&#xff1a;创建

旗帜分田(华为od机考题)

一、题目 1.原题 从前有个村庄&#xff0c;村民们喜欢在各种田地上插上小旗子&#xff0c;旗子上标识了各种不同的数字。 某天集体村民决定将覆盖相同数字的最小矩阵形的土地的分配给为村里做出巨大贡献的村民&#xff0c; 请问&#xff0c;此次分配土地&#xff0c;做出贡献…

DevEco Studio5 新建项目

Deveco Studio安装结束之后&#xff0c;开始新建一个项目&#xff1a; 1.点击Create Project新建项目 2.选择空的项目–>点击next 3.配置项目信息–>点击Finish 4.进入项目 6.右上方目前处于No Devices(无设备)状态–>点击下拉三角–>点击Devide Manager管理设…

Life long learning

现象&#xff1a;一个model进行multi-task learning做的还可以&#xff0c;说明模型是可以同时学会多个任务的&#xff0c;但是如果训练过程是1个task 1个task的顺序进行&#xff0c;模型就会发生灾难性的遗忘现象&#xff0c;只会做刚学完的task。 目标&#xff1a;让模型不要…

94.WEB渗透测试-信息收集-Google语法(8)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;93.WEB渗透测试-信息收集-Google语法&#xff08;7&#xff09; • Filetype • Filetype…

二叉树前序,中序,后序非递归遍历(Java)

1. 思路&#xff1a; 首先创建一个栈和顺序表&#xff0c;按照根左右的前序遍历顺序去遍历这棵树&#xff0c;一直往左孩子方向遍历&#xff0c;每遍历到一个结点就入栈并且加入到顺序表里&#xff0c;如果没有左孩子了&#xff0c;就拿出栈顶元素&#xff0c;看它是否有右孩子…

2024年牛客网最全1000道Java中高级面试题包含答案详解,看完稳了

我相信大多 Java 开发的程序员或多或少经历过 BAT 一些大厂的面试&#xff0c;也清楚一线互联网大厂 Java 面试是有一定难度的&#xff0c;小编经历过多次面试&#xff0c;有满意的也有备受打击的。因此呢小编想把自己这么多次面试经历以及近期的面试真题来个汇总分析&#xff…

JavaScript进阶指南之Event Loop

JavaScript进阶指南之Event Loop 引言 简要介绍主题&#xff1a; 在JavaScript的世界中&#xff0c;Event Loop是一个核心机制&#xff0c;它决定了代码的执行顺序&#xff0c;尤其是在处理异步任务时。对于初学者来说&#xff0c;理解Event Loop的工作原理是迈向JavaScript进…

设计模式篇(DesignPattern - 结构型模式)(持续更新迭代)

目录 模式一&#xff1a;适配器模式 一、简介 二、案例一&#xff08;充电器问题&#xff09; 1. 类适配器 2. 对象适配器 3. 接口适配器 三、应用 1. HandlerAdapter&#xff08;SpringMVC 源码应用&#xff09; 四、总结 模式二&#xff1a;桥接模式 一、简介 1.…

20240829版图的层次

1 最常用 Esc&#xff1a;取消操作 i&#xff1a;插入元件版图 c&#xff1a;复制 m&#xff1a;移动 u&#xff1a;撤销上一步操作 q&#xff1a;查看属性 f&#xff1a;全局视图 e&#xff1a;显示设置&#xff08;图层、栅格、走线模式等&#xff09; r&#xff1a;矩形填充…

纷享AI PaaS 助力打造企业级Al专属应用

纷享AIPaaS助力打造企业级Al专属应用 纷享销客人工智能(AI)在多个关键领域下的应用&#xff0c;诸如营销内容生成、线索预测评分、客服工作台助手等&#xff0c;均是通过AIPaaS平台来定制实现。 纷享销客AIPaaS平台通过AgentBuilder和ModelBuilder为上层的场景应用提供底层能…

005-CircuitBreaker断路器-Resilience4J

文章目录 1 CircuitBreaker1.1 实现原理1.2 一句话 2 Resilience4J2.1 是什么2.2 能干嘛2.3 怎么用 3 熔断(CircuitBreaker)(服务熔断服务降级)3.1 断路器三大状态3.2断路器3大状态之前的转换3.3断路器所有配置参数参考3.4 熔断降级案例需求说明3.5 COUNT_BASED(计数的滑动窗口…