创建线程的几种方法
在 Java 中创建和执行线程主要有以下四种方式。根据不同的业务场景我们需要权衡其灵活性、返回值需求以及资源开销。1. 继承Thread类这是最直观的线程创建方式。通过继承java.lang.Thread类并重写run()方法来定义线程的执行逻辑。示例代码public class MyThread extends Thread { Override public void run() { System.out.println(继承 Thread 类创建的线程: Thread.currentThread().getName()); } public static void main(String[] args) { MyThread thread new MyThread(); // 启动线程注意必须调用 start()调用 run() 只是普通方法调用不会启动新线程 thread.start(); } }特点分析优点编写简单在run()方法中直接使用this即可获取当前线程。缺点Java 仅支持单继承。如果当前类已经继承了其他父类如BaseEntity就无法再使用这种方式。2. 实现Runnable接口为了解决单继承的限制推荐使用实现java.lang.Runnable接口的方式。该方式实现了“任务逻辑Runnable”与“线程媒介Thread”的解耦。示例代码public class MyRunnable implements Runnable { Override public void run() { System.out.println(实现 Runnable 接口创建的线程: Thread.currentThread().getName()); } public static void main(String[] args) { // 创建任务实例 MyRunnable runnable new MyRunnable(); // 将任务传递给 Thread 对象并启动 new Thread(runnable, Runnable-Thread).start(); // 简写使用 Lambda 表达式JDK 8 推荐 new Thread(() - System.out.println(Lambda 线程: Thread.currentThread().getName())).start(); } }特点分析优点规避了单继承的局限性。适合多个相同程序代码的线程去处理同一个资源把虚拟的 CPU线程与数据、代码任务分离开更好地体现了面向对象的思想。缺点run()方法没有返回值且无法向外抛出受检异常Checked Exception。3. 实现Callable接口 FutureTask如果任务执行完毕后需要获取返回结果或者需要抛出异常可以使用java.util.concurrent.Callable接口。它需要配合FutureTask来使用。示例代码import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class MyCallable implements CallableInteger { Override public Integer call() throws Exception { System.out.println(Callable 开始计算...); Thread.sleep(1000); // 模拟耗时操作 return 42; // 支持返回值 } public static void main(String[] args) { // 1. 创建 Callable 任务 MyCallable callable new MyCallable(); // 2. 使用 FutureTask 包装 CallableFutureTask 实现了 RunnableFuture它既是 Runnable又是 Future FutureTaskInteger futureTask new FutureTask(callable); // 3. 放入 Thread 中执行 new Thread(futureTask).start(); try { // 4. 获取返回结果。注意get() 方法是阻塞性的会等待线程执行完毕才返回 Integer result futureTask.get(); System.out.println(线程执行结果为: result); } catch (Exception e) { e.printStackTrace(); } } }特点分析优点支持获取线程执行的返回值。允许call()方法声明抛出异常便于在外层捕获处理。支持取消任务。缺点实现步骤相对繁琐在调用get()方法时如果任务未完成会导致主线程阻塞。4. 使用线程池 (ExecutorService)在企业级实际开发中绝对不允许在代码中显式地new Thread()来创建线程。因为频繁地创建和销毁线程会消耗大量系统资源。我们必须通过线程池来管理和复用线程。示例代码import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolDemo { public static void main(String[] args) { // 推荐通过 ThreadPoolExecutor 手动创建线程池避免使用 Executors 快捷方法导致 OOM ExecutorService threadPool new ThreadPoolExecutor( 2, // 核心线程数 5, // 最大线程数 60, TimeUnit.SECONDS, // 线程空闲存活时间 new LinkedBlockingQueue(100), // 任务队列 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); // 提交无返回值的 Runnable 任务 threadPool.execute(() - { System.out.println(线程池执行无返回值任务: Thread.currentThread().getName()); }); // 提交有返回值的 Callable 任务并获取 Future var future threadPool.submit(() - { System.out.println(线程池执行有返回值任务...); return Success; }); try { System.out.println(返回结果: future.get()); } catch (Exception e) { e.printStackTrace(); } // 优雅关闭线程池 threadPool.shutdown(); } }特点分析优点降低资源消耗重复利用已创建的线程减少创建和销毁的开销。提高响应速度任务到达时无需等待线程创建即可立即执行。提高线程的可管理性防止无限制创建线程导致系统崩溃OOM可以进行统一的分配、调优和监控。缺点参数配置复杂需要根据具体的业务CPU 密集型或 IO 密集型合理配置核心参数。核心对比与选型指南维度继承 Thread实现 Runnable实现 Callable线程池 (Recommended)单继承限制有限制无法继承其他类无限制无限制无限制是否有返回值否否是视提交的方法而定 (submit/execute)是否能抛出异常否只能内部捕获否只能内部捕获是视具体执行任务而定资源消耗极高每次需 new极高每次需 new极高每次需 new极低线程复用适用场景极其简单的测试 Demo简单的、无返回值的多线程任务需要获取异步执行结果的单次任务所有的生产环境及实际业务项目终极面试考点 避坑指南start()与run()的区别调用start()方法会使线程进入就绪状态Runnable等待 CPU 调度后真正启动新线程执行run()。直接调用run()只是普通的同步方法调用依然在当前主线程中运行无法达到并发效果。为什么不允许使用Executors创建线程池阿里 Java 开发手册中明确指出Executors.newCachedThreadPool()和newScheduledThreadPool()允许创建的线程数量为Integer.MAX_VALUE可能会创建大量线程导致 OOM内存溢出。Executors.newFixedThreadPool()允许的队列长度为Integer.MAX_VALUE可能会堆积大量请求导致 OOM。因此必须通过ThreadPoolExecutor手动显式创建明确核心参数。