news 2026/4/26 12:00:25

java面试必问27:创建线程的3种方式:从基础到线程池,掌握原理不迷路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
java面试必问27:创建线程的3种方式:从基础到线程池,掌握原理不迷路

创建线程的 3 种方式:从基础到线程池,一篇讲透

面试官:“Java 中有几种方式创建线程?”
你:“三种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口(带返回值)。但实际开发中推荐使用线程池,避免频繁创建销毁线程的开销。”
面试官:“那 Runnable 和 Callable 有什么区别?线程池有哪些好处?”
你:“……”

很多人能说出三种方式,但一追问底层原理、区别、线程池优势就含糊了。本文从代码示例到原理分析,彻底讲透 Java 创建线程的几种方式。


一、概述

在 Java 中,线程是并发执行的基本单元。创建线程的三种主流方式:

方式返回结果能否抛出异常特点
继承 Thread简单,但单继承限制
实现 Runnable更灵活,推荐
实现 Callable有(Future)可以可获取返回结果,支持异常

无论哪种方式,核心都是创建一个Thread对象并调用start()方法,由 JVM 调度执行。


二、方式一:继承 Thread 类

1. 实现步骤

  • 定义一个类继承Thread
  • 重写run()方法,编写需要执行的任务。
  • 创建该类的实例,调用start()启动线程。

2. 代码示例

publicclassMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("线程 "+Thread.currentThread().getName()+" 执行");}}// 使用MyThreadt=newMyThread();t.start();// 启动线程,JVM 调用 run()

3. 缺点

  • 单继承限制:Java 不支持多继承,如果已经继承了其他类,就无法再使用此方法。
  • 任务与线程耦合:不利于任务共享和复用。
  • 无法获得返回值、无法抛异常。

三、方式二:实现 Runnable 接口

1. 实现步骤

  • 定义类实现Runnable接口,重写run()方法。
  • 创建Thread对象,将Runnable实例作为构造参数传入。
  • 调用Thread.start()启动。

2. 代码示例

publicclassMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("线程 "+Thread.currentThread().getName()+" 执行");}}// 使用Runnabletask=newMyRunnable();Threadt=newThread(task);t.start();

3. 优点

  • 避免了单继承的限制(可以实现多个接口)。
  • 任务与线程解耦,同一个Runnable可以被多个线程执行,易于资源共享。
  • 更适合线程池(线程池直接接受RunnableCallable)。

4. Runnable 与 Thread 的对比

  • Thread本身实现了Runnable接口,但通常我们选择实现Runnable
  • 实现Runnable将任务代码与线程运行机制分离,符合面向接口设计原则。

四、方式三:实现 Callable 接口

1. 特点

  • 位于java.util.concurrent包。
  • 有返回值(通过Future获取)。
  • 可以抛出受检异常。
  • 通常与ExecutorServiceFuture配合使用。

2. 实现步骤

  • 定义类实现Callable<V>,重写call()方法。
  • Callable实例提交给ExecutorService,返回Future对象。
  • 调用Future.get()获取执行结果(会阻塞直到完成)。

3. 代码示例

importjava.util.concurrent.*;publicclassMyCallableimplementsCallable<String>{@OverridepublicStringcall()throwsException{Thread.sleep(1000);return"执行结果:"+Thread.currentThread().getName();}}// 使用ExecutorServiceexecutor=Executors.newSingleThreadExecutor();Future<String>future=executor.submit(newMyCallable());try{Stringresult=future.get();// 阻塞等待System.out.println(result);}catch(InterruptedException|ExecutionExceptione){e.printStackTrace();}finally{executor.shutdown();}

4. 与 Runnable 的区别

特性RunnableCallable
方法run()call()
返回值无(void)有(V)
异常不能抛出受检异常可以抛出受检异常
使用方式ThreadExecutorService.submit(Runnable)ExecutorService.submit(Callable)

五、对比总结

方式代码复杂程度灵活性返回值适用场景
继承 Thread简单低(单继承)简单小任务,不推荐
实现 Runnable中等大多数并发任务,线程池基础
实现 Callable中等需要返回结果或抛出异常的任务

六、为什么不建议直接创建线程?—— 线程池的优势

直接new Thread().start()创建线程在小型 Demo 中可行,但生产环境存在严重问题:

  1. 资源消耗:每次创建和销毁线程都需要系统调用(内核态切换),开销大。
  2. 无限制创建导致 OOM:并发量大时,无限创建线程会耗尽内存或导致系统崩溃。
  3. 缺乏统一管理:线程无法复用,也没有监控、限流、定时等功能。

线程池(Executor framework)的优势

  • 线程复用:核心线程一直存活,任务执行完不销毁,减少创建销毁开销。
  • 流量控制:通过队列和最大线程数限制并发,防止资源耗尽。
  • 功能丰富:支持定时任务、周期任务、提交 Callable 返回 Future。
  • 优雅关闭:可以平滑关闭线程池,等待已有任务完成。

示例:使用线程池执行 Runnable 和 Callable

ExecutorServicepool=Executors.newFixedThreadPool(10);// 执行 Runnablepool.execute(()->System.out.println("Runnable task"));// 提交 Callable 并获取 FutureFuture<Integer>future=pool.submit(()->{Thread.sleep(1000);return42;});pool.shutdown();

实际开发中,几乎不会手动创建 Thread,而是通过 Spring 的线程池或自己配置 ThreadPoolExecutor。


七、常见面试追问

Q1:start()run()的区别?

  • run()只是普通方法调用,不会启动新线程。
  • start()会启动新线程,由 JVM 调度执行run()方法。

Q2:Runnable 和 Callable 如何选择?

  • 不需要返回值或抛异常 →Runnable
  • 需要返回值或抛异常 →Callable+Future

Q3:线程池有哪几种?如何配置?

常用的工厂方法:

  • newFixedThreadPool(int n):固定线程数。
  • newCachedThreadPool():按需创建,空闲线程存活 60 秒。
  • newSingleThreadExecutor():单线程。
  • newScheduledThreadPool(int corePoolSize):定时/周期任务。

实际生产中,建议直接使用ThreadPoolExecutor构造方法自定义核心参数(corePoolSize、maximumPoolSize、keepAliveTime、workQueue、handler),避免无界队列导致 OOM。

Q4:如何获取线程执行结果?

使用Callable+Future(或CompletableFuture)。

Q5:线程池的拒绝策略有哪些?

  • AbortPolicy(默认):抛出RejectedExecutionException
  • CallerRunsPolicy:让调用者线程自己执行。
  • DiscardPolicy:静默丢弃任务。
  • DiscardOldestPolicy:丢弃等待队列中最旧的任务,然后重试提交。

八、总结

方式核心要点
继承 Thread简单但单继承局限,不推荐
实现 Runnable解耦任务与线程,推荐
实现 Callable有返回值,可抛异常,配合线程池使用
线程池生产环境唯一推荐,避免自己 new Thread()

一句话记住创建线程的方式继承实现都可以,Runnable 更常用,Callable 带返回;线程池复用性能好,千万莫把手动搞

理解创建线程的方式及其背后的资源管理,是写出高可用并发程序的基础。希望这篇文章能帮你彻底掌握这个高频考点,欢迎继续讨论。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 11:55:29

Python机器学习聚类算法实战指南

1. Python机器学习聚类算法全面指南聚类分析是机器学习中最常用的无监督学习技术之一。作为一名从业多年的数据科学家&#xff0c;我发现聚类算法在实际业务场景中应用极为广泛——从客户细分到异常检测&#xff0c;从图像分割到社交网络分析。今天我将通过Python代码示例&…

作者头像 李华
网站建设 2026/4/26 11:50:44

MPLS跨域Option A、B、C怎么选?一张图看懂三种方案的区别与选型实战

MPLS跨域Option A/B/C实战选型指南&#xff1a;架构师必备的决策框架 当企业网络跨越多个运营商或大型自治系统时&#xff0c;MPLS VPN的互联方案选择往往成为网络架构师最头疼的问题。Option A的简单直接、Option B的折中平衡、Option C的高度扩展&#xff0c;每种方案背后都代…

作者头像 李华
网站建设 2026/4/26 11:49:16

告别硬件调试烦恼:用Wokwi在线模拟器5分钟搞定U8g2菜单和按键交互

嵌入式开发者的效率革命&#xff1a;Wokwi在线模拟器与U8g2菜单交互实战指南 当硬件调试成为阻碍创意落地的绊脚石时&#xff0c;一种全新的开发范式正在改变嵌入式开发的游戏规则。想象一下&#xff1a;凌晨三点&#xff0c;你的咖啡已经见底&#xff0c;但那个顽固的OLED屏幕…

作者头像 李华
网站建设 2026/4/26 11:47:24

用ADC0832给51单片机做个简易电压表(附完整代码和接线图)

从零打造51单片机电压表&#xff1a;ADC0832实战指南与避坑手册 项目背景与核心价值 在电子设计领域&#xff0c;模数转换器&#xff08;ADC&#xff09;如同连接现实世界与数字世界的桥梁。ADC0832作为经典的8位串行ADC芯片&#xff0c;以其亲民的价格和稳定的性能&#xff0c…

作者头像 李华