面试官让说出8种创建线程的方式,我只说了4种,然后挂了。。。

news2025/1/18 17:11:21

写在开头

昨天有个小伙伴私信说自己面试挂在了“Java有几种创建线程的方式”上,我问他怎么回答的,他说自己有背过八股文,回答了:继承Thread类、实现Runnable接口、实现Callable接口、使用线程池这四种,但是面试官让说出8种创建方式,他没说出来,面试就挂了,面试官给的理由是:只关注八股文背诵,对线程的理解不够深刻!

在这里想问一下大家,这位小伙伴回答的这四种有问题吗?看过《Java核心技术卷》和《Java编程思想》的朋友应该都知道,在这两本书中对于多线程编程都有详细的介绍,并且也都提到了线程创建的方式:

  • ①继承Thread类,并重写run()方法;
  • ②实现Runnable接口,并传递给Thread构造器;
  • ③实现Callable接口,创建有返回值的线程;
  • ④使用Executor框架创建线程池。

鉴于这两本书的权威性,以及在国内的广泛传播,让很多学习者,写书者,教学者都以此为标准,长此以往,这种回答似乎就成了一种看似完美的标准答案了。

因此,这位小伙伴的回答在大部分面试官那里都是正确的,没有什么大问题,但既然这位面试官抛出了8种的提问,很明显他要的回答并不是八股文参考答案。那应该怎么回答才能征服这位面试官呢?请接着往下看!

创建线程的10种方式

既然面试官想看线程创建的方式,我们就往上整,不仅仅他要的8种,我们还可以说出10种,甚至更多,今天花了点时间,梳理了一下之前用到过得以及网上看到的线程创建的办法,我们通过一个个小demo去感受一下。🥰
在这里插入图片描述

① 继承Thread类,并重写run()方法

这是最基本的一个线程创建的方式,闲话少叙,直接上代码!

【代码示例1】

public class Test {
    public static void main(String[] args) {
        new ThreadTest().start();
    }
}

//继承 Thread,重写 run() 方法
class ThreadTest extends Thread {
    @Override
    public void run() {
        for (int i = 0; i <3; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

输出:

Thread-0:0
Thread-0:1
Thread-0:2

创建一个ThreadTest 并继承Thread类,重写run方法,来创建一个线程,当然我们还可以采用匿名内部类去重写run方法来创建线程,这其实也可以算所一种方式

【代码示例2】

public class Test {
    public static void main(String[] args) {
        new Thread("t1"){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();
    }
}
//打印结果:t1

② 实现Runnable接口

这也是常用的四个方式之一,实现Runnable接口并重写run方法。

【代码示例3】

public class Test implements Runnable{

    public static void main(String[] args) {
        Test test = new Test();
        new Thread(test).start();
    }

    @Override
    public void run() {
        System.out.println("我是Runnable线程");
    }
}
//打印结果:我是Runnable线程

③ 实现Callable接口

这种方式实现Callable接口,可以创建有返回值的线程。

【代码示例4】

public class Test implements Callable<String> {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test test = new Test();
        FutureTask<String> stringFutureTask = new FutureTask<>(test);
        new Thread(stringFutureTask).start();
        System.out.println(stringFutureTask.get());
    }
    
    @Override
    public String call() throws Exception {
        return "我是线程Callable";
    }
}
//打印结果:我是线程Callable

这个示例里使用了FutureTask,这个类可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果。

④ 使用ExecutorService线程池

通过Executors创建线程池,Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,它就是创建线程池的,但是很多的大厂已经不建议使用该类去创建线程池。原因在于,该类创建的很多线程池的内部使用了无界任务队列,在并发量很大的情况下会导致 JVM 抛出 OutOfMemoryError,直接让 JVM 崩溃,影响严重。因此,在这里我们只将它作为一个案例参考,真实开发中不建议使用!

【代码示例5】

public class Test  {
    public static void main(String[] args) {
        // 使用工具类 Executors 创建单线程线程池,其实还有其他几种创建方式
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        //提交执行任务
        singleThreadExecutor.submit(() -> {System.out.println("单线程线程池执行任务");});
        //关闭线程池
        singleThreadExecutor.shutdown();
    }
}
//打印结果:单线程线程池执行任务

⑤ 使用CompletableFuture类

CompletableFuture是JDK1.8引入的新类,CompletableFuture 除了提供了更为好用和强大的 Future 特性之外,还提供了函数式编程、异步任务编排组合(可以将多个异步任务串联起来,组成一个完整的链式调用)等能力。后面的文章更新中会详说,现在先上代码!

【代码示例6】

public class Test  {

    public static void main(String[] args) throws InterruptedException {
        CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + ":"+"CompletableFuture");
            return "CompletableFuture";
        });
        // 需要阻塞,否则看不到结果
        Thread.sleep(1000);
    }
}
//打印结果:ForkJoinPool.commonPool-worker-1:CompletableFuture

⑥ 基于ThreadGroup线程组

在Java的线程中同样有组的概念,可以通过ThreadGroup创建一个线程组,在线程组中创建多个线程。

【代码示例7】

public class Test  {

    public static void main(String[] args) {

        ThreadGroup group = new ThreadGroup("groupName");
        new Thread(group, ()->{
            System.out.println("T1......");
        }, "T1").start();

        new Thread(group, ()->{
            System.out.println("T2......");
        }, "T2").start();

        new Thread(group, ()->{
            System.out.println("T3......");
        }, "T3").start();
    }
}

输出:

T1......
T2......
T3......

⑦ 使用FutureTask类

看到这个FutureTask类是不是很熟悉,对喽!咱们在第三种方式,实现Callable接口,重写call方法中也用到了它,他们的实现方式几乎都万变不离其宗,只不过我们在这里采用了lambda 表达式调用。

【代码示例8】

public class Test  {
    public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName()+":"+"futureTask");
            return "futureTask";
        });
        new Thread(futureTask).start();
    }
}
//执行结果:Thread-0:futureTask

其实虽然是匿名方式,它的底部仍然调用了callable。我们来看一下FutureTask底层的构造方法,都是通过传参或者调用callable。

【源码解析1】

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
    // 通过适配器RunnableAdapter来将Runnable对象runnable转换成Callable对象
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;
}

⑧ 使用匿名内部类或Lambda表达式

这种方式其实在上面的实现中多少都有提到,匿名方式创建,lambda 表达式创建。

【代码示例9】

public class Test  {

    public static void main(String[] args) {
        //new Runnable 对象,匿名重写 run() 方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名创建线程");
            }
        }).start();
        //JDK 1.8 开始支持 lambda 表达式
        new Thread(() ->
                System.out.println("lambda创建线程")
        ).start();
    }
}

⑨ 使用Timer定时器类

Timer类在JDK1.3时被引入,用来执行定时任务,里面需要传入两个数字,第一个代表启动后多久开始执行,第二个代表每间隔多久执行一次,单位是ms毫秒。

【代码示例10】

public class Test  {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器线程");
            }
        }, 0, 1000);
    }
}

⑩ 使用ForkJoin线程池或Stream并行流

ForkJoin是JDK1.7引入的新线程池,基于分治思想实现。而后续JDK1.8的parallelStream并行流,默认就基于ForkJoin实现,我们直接上代码感受一下。

【代码示例11】

public class Test  {

    public static void main(String[] args) {
        //ForkJoinPool线程池
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        forkJoinPool.execute(()->{
            System.out.println(Thread.currentThread().getName()+":"+"ForkJoinPool线程池");
        });
        //parallelStream流
        List<String> list = Arrays.asList(Thread.currentThread().getName()+":"+"parallelStream流");
        list.parallelStream().forEach(System.out::println);
    }
}

输出:

ForkJoinPool-1-worker-1:ForkJoinPool线程池
//并行流在主线程中被打印。
main:parallelStream流

总结

OK,我们根据面试官的需求,写出了10种创建线程的方式,如果再细分,甚至还可以更多,毕竟线程池的工具类还有没往上写的呢。

那么,我们一起静默3分钟,好好思考一下,在Java中创建一个线程的本质,真的是八股文中所说的3种、4种、8种,甚至更多吗?Build哥认为,真正创建线程的方式只有1种,其他的衍生品都算套壳!

考虑到本篇已经六七千字了,所以我们在下一篇文章中来分析一下为什么“真正创建线程的方式只有1种!”

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在这里插入图片描述

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

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

相关文章

艺术与科技的结合,AI绘画图生图怎么样?

AI绘画图生图是指通过人工智能技术生成的具有艺术价值的图像。它可以根据用户提供的参考图像或描述&#xff0c;自动生成具有艺术风格的新图像。这些图像可以是风景、人物、抽象画等各种形式。那么ai绘画图生图到底怎么样&#xff1f; AI绘画图生图的优点在于它可以快速、高效地…

产品展示型wordpress外贸网站模板

孕婴产品wordpress外贸网站模板 吸奶器、待产包、孕妇枕头、护理垫、纸尿裤、孕妇装、孕婴产品wordpress外贸网站模板。 https://www.jianzhanpress.com/?p4112 床品毛巾wordpress独立站模板 床单、被套、毛巾、抱枕、靠垫、围巾、布艺、枕头、乳胶枕、四件套、浴巾wordpre…

【学习】DenseNet

DenseNet介绍 DenseNet模型的基本思路与ResNet一致&#xff0c;但它建立的是前面所有层与后面层的密集连接&#xff08;即相加变连结&#xff09;&#xff0c;它的名称也是由此而来。DenseNet的另一大特色是通过特征在通道上的连接来实现特征重用。这些特点让DenseNet的参数量…

C# CallerMemberName、CallerFilePath、CallerLineNumber

CallerMemberName&#xff1a;调用某个方法的主方法名称 CallerFilePath&#xff1a;调用某个方法的主方法所在的类文件地址 CallerLineNumber&#xff1a;调用这个方法所在的行号 用这三个附加属性&#xff0c;需要设置默认值。

推荐5款极具效率的实用工具软件

​ 每次分享实用的软件,都会给人一种踏实和喜悦的感觉,这也是我热衷于搜集和推荐高效工具软件的原因。 1.个人日记软件——EDiary ​ EDiary是一款功能丰富的个人日记软件&#xff0c;用户可以在不联网的状态下使用&#xff0c;保护隐私。它支持日记、记事本、日历、事件提醒…

QT----云服务器部署Mysql,Navicat连接1698 -Access denied for user ‘root‘@‘‘

阿里云有活动&#xff0c;白嫖了一年的新加坡轻量级服务器&#xff0c;有点卡&#xff0c;有时候要开梯子 白嫖300元优惠券 目录 1 安装启动Mysql服务2 更改连接权限2.1 Navicat连接报错1698 -Access denied for user root 3 qt连接云服务器数据库 1 安装启动Mysql服务 我使用…

Docker-部署若依项目

文章目录 后端一、搭建局域网二、redis安装测试 三、MySQL安装四、后端项目放入位置及使用Dockerfile自定义镜像后端项目放入位置 前端配置检查各个端口是否启动nginx部署 首先得先把内部的文件给删除清空 docker images–查看有哪些文件 docker rmi -f ID–删除ID 后端 一、…

linux 安装OpenRestry

一、OpenRestry官网 openRestry中文官网 openRestry英文官网 二、OpenRestry搭建 可以参考官方提供的网址进行搭建&#xff1a;OpenRestry安装官网 ​ 三、开始安装 1、安装依赖库 yum install libtermcap-devel ncurses-devel libevent-devel readline-devel pcre-deve…

Docker 创建容器并指定时区

目录 1. 通过环境变量设置时区&#xff08;推荐&#xff09;2. 挂载宿主机的时区文件到容器中3. 总结 要在 Docker 容器中指定时区&#xff0c;可以通过两种方式来实现&#xff1a; 1. 通过环境变量设置时区&#xff08;推荐&#xff09; 在 Docker 运行时&#xff0c;可以通…

简析:老阳分享的视频号带货蓝海项目前景如何?

随着社交媒体的快速发展&#xff0c;视频号带货已经成为电商领域的一大热点。近期&#xff0c;知名网红老阳分享了自己的视频号带货项目&#xff0c;引发了广泛关注。本文将从视频号前景、注意事项等方面&#xff0c;分析老阳分享的视频号带货项目前景如何。 首先&#xff0c;从…

三维GIS的业务导向

的确&#xff0c;目前三维GIS以做特效居多&#xff0c;酷炫、亮眼&#xff0c;从二维转到三维&#xff0c;第一眼就给人眼前一亮的感觉&#xff0c;就凭这一项&#xff0c;很多客户就会买单&#xff0c;GIS的客户以政府、科研院所、特种行业为主&#xff0c;买过一次单后&#…

10年架构师和CTO经验总结:从程序员到CTO

在这个不断变化的技术环境中&#xff0c;软件架构的重要性愈发凸显。然而作为一个职业&#xff0c;软 件架构师的地位却不断受到业内外的挑战。在20世纪90年代互联网技术发展初期&#xff0c;软件架 构师是备受推崇的职位。在相对简单的技术诉求下&#xff0c;他们的决策直接决…

【网络】:再谈传输层(UDP)

传输层 一.再谈端口号二.UDP 一.再谈端口号 端口号(Port)标识了一个主机上进行通信的不同的应用程序。 在TCP/IP协议中, 用 “源IP”, “源端口号”, “目的IP”, “目的端口号”, “协议号” 这样一个五元组来标识一个通信(可以通过netstat -n查看); 1.端口号划分 0 - 1023:…

【python 1】----Pytest基础知识

定义 用于编写和执行Python测试全功能测试框架&#xff08;工具&#xff09;&#xff0c;是一个第三方库 安装 pip insatll pytest 安装pytest --version 校验 pytest的组成构成 不写调用语句也可以执行函数内容 在用例运行语句里面&#xff1a; -s:指的是开启与终端的…

有一点好看的wordpress外贸独立站模板

手机配件wordpress外贸网站模板 充电器、移动电源、手机膜、手机电池、手机壳、手机转接头等手机配件wordpress外贸网站模板。 https://www.jianzhanpress.com/?p3809 车载电器wordpress外贸网站模板 车载吸尘器、空气净化器、行车记录仪、车载充电器、车载影音导航等车载电…

el-table 插入单选并进行校验

<template><div><el-form :model"list" ref"ruleForm"><el-table :data"list.tableData" style"width: 100%"><el-table-column prop"time" label"日期" width"180"><…

[pdf]DDD领域驱动设计批评-幻灯片合集(一)共190页

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 DDD领域驱动设计浮夸&#xff0c;Eric Evans开了个坏头 领域驱动设计伪创新 之 聚合根 哪些中文资料上有领域模型案例 领域驱动设计割裂历史&#xff0c;哪里有详细一些的真实历史 软件…

Hive Thrift Server

hive-site.xml配置文件 <property><name>hive.server2.thrift.bind.host</name><value>node1</value> </property>hive.server2.thrift.bind.host: This property determines the host address to which the HiveServer2 Thrift service …

可视化拖拽式小程序源码系统 带完整的安装代码包以及搭建教程

信息技术的快速发展&#xff0c;小程序已成为企业、个人开发者以及各类机构推广品牌、提供服务的重要载体。然而&#xff0c;对于许多非专业开发者来说&#xff0c;编写小程序源码往往是一个复杂且耗时的过程。小编给大家分享一款可视化拖拽式小程序源码系统&#xff0c;旨在帮…

Docker-自定义镜像

目录 1 前言 2 构建java应用的步骤及镜像结构图 2.1 构建步骤 2.2 镜像结构图 3 Dockerfile常用指令 4 Dockerfile的内容举例 4.1 一般形式 4.2 一般形式的优化 5 构建镜像 5.1 指令 5.2 实操 5.2.1 加载jdk镜像(基础镜像) 5.2.2 构建我们的镜像 5.2.3 使用我们的…