创建线程的 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() 执行);}}// 使用MyThreadtnewMyThread();t.start();// 启动线程JVM 调用 run()3. 缺点单继承限制Java 不支持多继承如果已经继承了其他类就无法再使用此方法。任务与线程耦合不利于任务共享和复用。无法获得返回值、无法抛异常。三、方式二实现 Runnable 接口1. 实现步骤定义类实现Runnable接口重写run()方法。创建Thread对象将Runnable实例作为构造参数传入。调用Thread.start()启动。2. 代码示例publicclassMyRunnableimplementsRunnable{Overridepublicvoidrun(){System.out.println(线程 Thread.currentThread().getName() 执行);}}// 使用RunnabletasknewMyRunnable();ThreadtnewThread(task);t.start();3. 优点避免了单继承的限制可以实现多个接口。任务与线程解耦同一个Runnable可以被多个线程执行易于资源共享。更适合线程池线程池直接接受Runnable或Callable。4. Runnable 与 Thread 的对比Thread本身实现了Runnable接口但通常我们选择实现Runnable。实现Runnable将任务代码与线程运行机制分离符合面向接口设计原则。四、方式三实现 Callable 接口1. 特点位于java.util.concurrent包。有返回值通过Future获取。可以抛出受检异常。通常与ExecutorService和Future配合使用。2. 实现步骤定义类实现CallableV重写call()方法。将Callable实例提交给ExecutorService返回Future对象。调用Future.get()获取执行结果会阻塞直到完成。3. 代码示例importjava.util.concurrent.*;publicclassMyCallableimplementsCallableString{OverridepublicStringcall()throwsException{Thread.sleep(1000);return执行结果Thread.currentThread().getName();}}// 使用ExecutorServiceexecutorExecutors.newSingleThreadExecutor();FutureStringfutureexecutor.submit(newMyCallable());try{Stringresultfuture.get();// 阻塞等待System.out.println(result);}catch(InterruptedException|ExecutionExceptione){e.printStackTrace();}finally{executor.shutdown();}4. 与 Runnable 的区别特性RunnableCallable方法run()call()返回值无void有V异常不能抛出受检异常可以抛出受检异常使用方式Thread或ExecutorService.submit(Runnable)仅ExecutorService.submit(Callable)五、对比总结方式代码复杂程度灵活性返回值适用场景继承 Thread简单低单继承无简单小任务不推荐实现 Runnable中等高无大多数并发任务线程池基础实现 Callable中等高有需要返回结果或抛出异常的任务六、为什么不建议直接创建线程—— 线程池的优势直接new Thread().start()创建线程在小型 Demo 中可行但生产环境存在严重问题资源消耗每次创建和销毁线程都需要系统调用内核态切换开销大。无限制创建导致 OOM并发量大时无限创建线程会耗尽内存或导致系统崩溃。缺乏统一管理线程无法复用也没有监控、限流、定时等功能。线程池Executor framework的优势线程复用核心线程一直存活任务执行完不销毁减少创建销毁开销。流量控制通过队列和最大线程数限制并发防止资源耗尽。功能丰富支持定时任务、周期任务、提交 Callable 返回 Future。优雅关闭可以平滑关闭线程池等待已有任务完成。示例使用线程池执行 Runnable 和 CallableExecutorServicepoolExecutors.newFixedThreadPool(10);// 执行 Runnablepool.execute(()-System.out.println(Runnable task));// 提交 Callable 并获取 FutureFutureIntegerfuturepool.submit(()-{Thread.sleep(1000);return42;});pool.shutdown();实际开发中几乎不会手动创建 Thread而是通过 Spring 的线程池或自己配置 ThreadPoolExecutor。七、常见面试追问Q1start()和run()的区别run()只是普通方法调用不会启动新线程。start()会启动新线程由 JVM 调度执行run()方法。Q2Runnable 和 Callable 如何选择不需要返回值或抛异常 →Runnable。需要返回值或抛异常 →CallableFuture。Q3线程池有哪几种如何配置常用的工厂方法newFixedThreadPool(int n)固定线程数。newCachedThreadPool()按需创建空闲线程存活 60 秒。newSingleThreadExecutor()单线程。newScheduledThreadPool(int corePoolSize)定时/周期任务。实际生产中建议直接使用ThreadPoolExecutor构造方法自定义核心参数corePoolSize、maximumPoolSize、keepAliveTime、workQueue、handler避免无界队列导致 OOM。Q4如何获取线程执行结果使用CallableFuture或CompletableFuture。Q5线程池的拒绝策略有哪些AbortPolicy默认抛出RejectedExecutionException。CallerRunsPolicy让调用者线程自己执行。DiscardPolicy静默丢弃任务。DiscardOldestPolicy丢弃等待队列中最旧的任务然后重试提交。八、总结方式核心要点继承 Thread简单但单继承局限不推荐实现 Runnable解耦任务与线程推荐实现 Callable有返回值可抛异常配合线程池使用线程池生产环境唯一推荐避免自己 new Thread()一句话记住创建线程的方式继承实现都可以Runnable 更常用Callable 带返回线程池复用性能好千万莫把手动搞。理解创建线程的方式及其背后的资源管理是写出高可用并发程序的基础。希望这篇文章能帮你彻底掌握这个高频考点欢迎继续讨论。