Java如何创建线程?到底有几种方式创建线程?

news2024/12/23 6:55:51

文章目录

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  • 匿名内部类形式的线程创建
  • 实现接口 VS 继承Thread
  • 到底有几种创建线程的方式?
  • 参考

继承Thread类

定义一个线程类,重写实现run方法(因为 Thread类也实现了 Runable接口),在其中定义线程要执行的任务(希望和其他线程并发执行的任务)。
当调用 start()方法 启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的run()方法

注:启动该线程要调用该线程的start方法,而不是run方法!!!

// 模板代码
public class MyThread extends Thread {
    public void run() {
        // ...
    }
}

public static void main(String[] args) {
    MyThread mt = new MyThread();
    mt.start();
}
// 完整示例
package thread;

/**
 * 多线程
 * 线程:程序中一个单一的顺序执行流程
 * 多线程:多个单一顺序执行流程"同时"执行
 *
 * 多线程改变了代码的执行方式,从原来的单一顺序执行流程变为多个执行流程"同时"执行。
 * 可以让多个代码片段的执行互不打扰。
 *
 * 线程之间是并发执行的,并非真正意义上的同时运行。
 * 常见线程有两种方式:
 * 1:继承Thread并重写run方法
 *
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //创建两个线程
        Thread t1 = new MyThread1();
        Thread t2 = new MyThread2();
        /*
            启动线程,注意:不要调用run方法!!
            线程调用完start方法后会纳入到系统的线程调度器程序中被统一管理。
            线程调度器会分配时间片段给线程,使得CPU执行该线程这段时间,用完后
            线程调度器会再分配一个时间片段给一个线程,如此反复,使得多个线程
            都有机会执行一会,做到走走停停,并发运行。
            线程第一次被分配到时间后会执行它的run方法开始工作。
         */
        t1.start();
        t2.start();

    }
}
/**
 * 第一种创建线程的优点: 结构简单,利于匿名内部类形式创建。
 *
 * 缺点:
 * 1:由于java是单继承的,这会导致继承了Thread就无法再继承其他类去复用方法
 * 2:定义线程的同时重写了run方法,这等于将线程的任务定义在了这个线程中导致线程只能干这件事。重(chong)用性很低。
 */
class MyThread1 extends Thread{
    public void run(){
        for (int i=0;i<1000;i++){
            System.out.println("hello姐~");
        }
    }
}
class MyThread2 extends Thread{
    public void run(){
        for (int i=0;i<1000;i++){
            System.out.println("来了~老弟!");
        }
    }
}

实现Runnable接口

需要实现run()方法。通过 Thread 调用start()方法来启动线程。

// 模板代码
public class MyRunnable implements Runnable {
    public void run() {
        // ...
    }
}

public static void main(String[] args) {
    MyRunnable instance = new MyRunnable();
    Thread thread = new Thread(instance);
    thread.start();
}
// 完整示例
package thread;

/**
 * 第二种创建线程的方式
 * 实现Runnable接口单独定义线程任务
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        //实例化任务
        Runnable r1 = new MyRunnable1();
        Runnable r2 = new MyRunnable2();
        //创建线程并指派任务
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
    }
}
class MyRunnable1 implements Runnable{
    public void run() {
        for (int i=0;i<1000;i++){
            System.out.println("你是谁啊?");
        }
    }
}
class MyRunnable2 implements Runnable{
    public void run() {
        for (int i=0;i<1000;i++){
            System.out.println("开门!查水表的!");
        }
    }
}

实现Callable接口

与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。

// 模板代码
public class MyCallable implements Callable<Integer> {
    public Integer call() {
        return 123;
    }
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable mc = new MyCallable();
    FutureTask<Integer> ft = new FutureTask<>(mc);
    Thread thread = new Thread(ft);
    thread.start();
    System.out.println(ft.get());
}

匿名内部类形式的线程创建

package thread;

/**
 * 使用匿名内部类完成线程的两种创建
 */
public class ThreadDemo3 {
    public static void main(String[] args) {
        Thread t1 = new Thread(){
            public void run(){
                for(int i=0;i<1000;i++){
                    System.out.println("你是谁啊?");
                }
            }
        };
        
       /*Runnable r2 = new Runnable() {
           public void run() {
               for(int i=0;i<1000;i++){
                   System.out.println("我是查水表的!");
               }
           }
       };*/
        
        //Runnable可以使用lambda表达式创建
        Runnable r2 = ()->{
                for(int i=0;i<1000;i++){
                    System.out.println("我是查水表的!");
                }
        };


        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
    }
}

实现接口 VS 继承Thread

实现接口会更好一些,因为:

  • Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
  • 类可能只要求可执行就行,继承整个 Thread 类开销过大

到底有几种创建线程的方式?

我们先看下 JDK 源码中对 Thread 类的一段解释,如下图。
在这里插入图片描述
这里说的两种方式就是 继承Thread类实现Runnable接口 两种方式。但是,我们会发现这两种方式,最终都会调用Thread.start方法,而 start方法最终会调用run方法
不同的是,在实现 Runnable 接口的方式中,调用的是 Thread 本类的 run 方法。我们看下它的源码,
在这里插入图片描述
这种方式,会把创建的 Runnable 实现类对象赋值给 target ,并运行 target 的 run 方法。
再看继承Thread类的方式,我们同样需要调用 Thread 的 start 方法来启动线程。由于子类重写了 Thread 类的 run 方法,因此最终执行的是这个子类的 run 方法。

所以,我们也可以这样说。在本质上,创建线程只有一种方式,就是构造一个Thread类(其子类其实也可以认为是一个 Thread 类)。

构造 Thread类 又有两种方式,一种是继承 Thread 类,一种是实现 Runnable接口。其最终都会创建 Thread 类(或其子类)的对象。

再来看实现 Callable ,结合 Future 和 FutureTask 的方式。可以发现,其最终也是通过new Thread(task)的方式构造 Thread 类。
最后,在线程池中,我们其实是把创建和管理线程的任务都交给了线程池。而创建线程是通过线程工厂类 DefaultThreadFactory 来创建的(也可以自定义工厂类)。我们看下这个工厂类的具体实现。
在这里插入图片描述
它会给线程设置一些默认值,如线程名称,线程的优先级,线程组,是否是守护线程等。最后还是通过new Thread()的方式来创建线程的。

因此,综上所述。在回答这个问题的时候,我们可以说本质上创建线程就只有一种方式,就是构造一个 Thread 类。

如果你要说有 1种、2种、3种、4种 其实也是可以的。重要的是,你要能说出你的依据,讲出它们各自的不同点和共同点。讲得头头是道,让面试官对你频频点头。
说只有构造 Thread 类这一种创建线程方式,个人认为还是有些牵强。因为,无论你从任何手段出发,想创建一个线程的话,最终肯定都是构造 Thread 类。(包括以上几种方式,甚至通过反射,最终不也是 newInstance 么)。
那么,如果按照这个逻辑的话,我就可以说,不管创建任何的对象(Object),都是只有一种方式,即构造这个对象(Object) 类。这个结论似乎有些太过无聊了,因为这是一句非常正确的废话。
以 ArrayList 为例,我问你创建 ArrayList 有几种方式。你八成会为了炫耀自己知道的多,跟我说,
1.通过构造方法,List list = new ArrayList();
2.通过 Arrays.asList(“a”, “b”);
3.通过Java8提供的Stream API,如 List list = Stream.of(“a”, “b”).collect(Collectors.toList());
4.通过guava第三方jar包,List list3 = Lists.newArrayList(“a”, “b”);
等等,仅以上就列举了四种。现在,我告诉你创建 ArrayList 就只有一种方式,即构造一个 ArrayList 类,你抓狂不。

参考

https://cloud.tencent.com/developer/article/1739160

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

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

相关文章

Pytorch神经网络的参数管理

目录 一、参数访问 1、目标参数 2、一次性访问所有参数 3、从嵌套块收集参数 二、参数初始化 1、内置初始化 2、自定义初始化 3、参数绑定 在选择了架构并设置了超参数后&#xff0c;我们就进入了训练阶段。此时&#xff0c;我们的目标是找到使损失函数最小化的模型参数…

矩阵式键盘实现的电子密码锁

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit P14P1^4; //将P14位定义为P1.4引脚 sbit P15P1^5; //将P15位定义为P1.5引脚 sbit P16P1^6; //将P16位定义为P1.6引脚 sbit P17P1^7; //将P17位定义为P1.7引脚 sbit soundP3^7; //将so…

新媒体宣传与广州迅腾文化传播有限公司:品牌知名度提升的新动力

新媒体宣传与广州迅腾文化传播有限公司&#xff1a;品牌知名度提升的新动力 随着科技的飞速发展和互联网的普及&#xff0c;新媒体已经成为现代社会不可或缺的一部分。新媒体平台具有传播速度快、覆盖面广的特点&#xff0c;为企业品牌宣传提供了前所未有的机会。广州迅腾文化…

黑马点评07 秒杀优化 加阻塞队列

实战篇-22.秒杀优化-异步秒杀思路_哔哩哔哩_bilibili 1.流程回顾 1.1超卖问题 判断秒杀时间&#xff0c;加乐观锁&#xff08;比较标记/版本&#xff09;&#xff0c;检查库存是否大于0 1.2一人一单问题 看看数据库里有没有这个这个人下的订单&#xff1a; 1.单机模式中…

自动化测试 (五) 读写64位操作系统的注册表

自动化测试经常需要修改注册表 很多系统的设置&#xff08;比如&#xff1a;IE的设置&#xff09;都是存在注册表中。 桌面应用程序的设置也是存在注册表中。 所以做自动化测试的时候&#xff0c;经常需要去修改注册表 Windows注册表简介 注册表编辑器在 C:\Windows\regedit…

第二百一十五回 如何创建单例模式

文章目录 1. 概念介绍2. 思路与方法2.1 实现思路2.2 实现方法 3. 示例代码4. 内容总结 我们在上一章回中介绍了"分享三个使用TextField的细节"沉浸式状态样相关的内容&#xff0c;本章回中将介绍 如何创建单例模式.闲话休提&#xff0c;让我们一起Talk Flutter吧。 …

@KafkaListener 注解配置多个 topic

见如下示例 主要见 KafkaListener 中 topics 属性的配置 其中 ${xxxx.topic1} 为从springBoot 配置文件中读取的属性值 KafkaListener(topics {"${xxxx.topic1}", "${xxxx.topic2}"}, groupId "${xxxx.groupId}",containerFactory "xxx…

易点易动打通OA系统,实现固定资产高效管理

近年来,随着信息化建设的不断深入,OA系统在企业管理工作中的应用也日趋广泛。传统的固定资产管理存在数据分散,管理效率低等问题。深度整合易点易动和OA系统,可以打通各系统之间的数据通道,实现固定资产通过OA系统的全流程管理。这不仅可以提升管理效率,减轻人工管理成本,也更方…

部署LVS的NAT模式

实验准备 #负载调度器# 192.168.116.40 #内网 12.0.0.100 #外网 先添加双网卡 #web服务器# 192.168.116.20 #web1 192.168.116.30 #web2 #nfs共享服务# 192.168.116.10 #nfs systemctl stop firewalld setenforce 0 1.nfs共享文件 1…

Python-Selenium-使用 pywinauto 实现 Input 上传文件

当前环境&#xff1a;Win10 Python3.7 pywinauto0.6.8&#xff0c;selenium3.14.1 示例代码 from pywinauto import Desktop import osapp Desktop() dialog app[打开] dialog[Edit].set_edit_text(os.getcwd() .\\example-01.jpg) dialog[Button].click() 其他方法&…

接口测试的工具(3)----postman+node.js+newman

1.安装newman&#xff1a;输入命令之后 一定注意 什么都不要操作 静静的等待结束就行了。 2.安装失败的对此尝试不行 在用下面的方法 解压一下就行了 3.验证是否成功 多次尝试是可以在线安装成功的

SpringCloudAliBaba篇之Seata:分布式事务组件理论与实践

1、事务简介 事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中&#xff0c;一个事务由一组SQL语句组成&#xff0c;事务具有4个属性&#xff1a;原子性、一致性、隔离性、持久性。这四个属性通常称为ACID原则。 原子性(atomici…

在非联网、无网络环境下,fpm的安装和生成RPM包的使用案例

文章目录 前言1、安装fpm1.1、安装Ruby环境1.2、gem 安装 fpm 2、fpm使用2.1、fpm常用参数2.2、fpm使用案例2.2.1、fpmFirstDemo文件夹2.2.3、编写脚本文件2.2.4、生成RPM包2.2.5、RPM安装与卸载测试 前言 由于fpm采用Ruby语言开发&#xff0c;因此在使用之前需要先在您的虚拟…

AI日报:OpenAI扩大创业基金计划

欢迎订阅专栏 《AI日报》 获取人工智能邻域最新资讯 文章目录 OpenAI拓宽Converge启动程序变压器模型背后的思想建立启动融资新闻AutoGen AI支点其他 OpenAI拓宽Converge启动程序 ChatGPT制造商OpenAI正在扩大其Converge AI创业计划。 OpenAI的Converge产品于2022年12月首次…

力扣225. 用队列实现栈【附进阶版】

文章目录 力扣225. 用队列实现栈示例思路及其实现两个队列模拟栈一个队列模拟栈 力扣225. 用队列实现栈 示例 思路及其实现 两个队列模拟栈 队列是先进先出的规则&#xff0c;把一个队列中的数据导入另一个队列中&#xff0c;数据的顺序并没有变&#xff0c;并没有变成先进后…

【Python】—— 如果使用matplotlib做数据可视化

matplotlib做数据可视化 相关知识掌握matplotlib的基本使用方法1. 折线图2. 散点图3. 柱状图4. 饼图5. 直方图6. 等高线图7. 图形定制 掌握数据处理的基本方法1. 数据筛选2. 缺失值处理3. 异常值处理 理解数据可视化的原则和方法1. 选择合适的图表类型2. 避免数据混淆3. 突出重…

继电器的工作原理及驱动电路

继电器是具有隔离功能的自动开关元件&#xff0c;广泛应用于遥控、遥测、通讯、自动控制、机电一体化及电力电了设备中&#xff0c;是最重要的控制元件之一。继电器实际上是用较小的电流去控制较大电流的一种“自动开关”。故在电路中起着自动调节、安全保护、转换电路等作用。…

camera曝光时间

曝光和传感器读数 相机上的图像采集过程由两个不同的部分组成。第一部分是曝光。曝光完成后&#xff0c;第二步就是从传感器的寄存器中读取数据并传输&#xff08;readout&#xff09;。 曝光&#xff1a;曝光是图像传感器进行感光的一个过程&#xff0c;相机曝光时间&#xf…

《PySpark大数据分析实战》-12.Spark on YARN配置Spark运行在YARN上

&#x1f4cb; 博主简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是wux_labs。&#x1f61c; 热衷于各种主流技术&#xff0c;热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员&#xff08;PCTA&#xff09;、TiDB数据库专家&#xff08;PCTP…

创建型设计模式 | 原型模式

一、原型模式 1、原理 原型模式&#xff0c;用原型实例指定创建对象的种类&#xff0c;并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象&#xff0c;而且不需要知道任何创建的细节。原型像是一个模板&#xff0c;可以基于它复制好多…