Java 开发必看!多线程创建 3 种核心方式 + 2 种进阶方案讲透

Java 开发必看!多线程创建 3 种核心方式 + 2 种进阶方案讲透

java多线程程序

作为 Java 开发,你是不是也遇到过这种情况?明明代码本地跑起来没问题,一上线就出现奇怪的线程安全问题 —— 要么数据计算错乱,要么接口响应突然变慢,排查半天发现,竟然是最开始创建多线程的方式选错了!

我之前帮同事排查过一个订单处理的 bug,明明单测时订单状态更新都正常,线上高峰期却总有几笔订单状态 “卡住”。最后定位到代码里java多线程程序,他用继承 类的方式创建线程,还在子类里加了共享变量,结果多线程并发时变量被反复覆盖,导致状态同步出了问题。其实如果最开始选实现 接口的方式,这类问题完全可以避免。

所以今天咱们就好好聊聊 Java 里创建多线程的那些事儿,从基础到进阶一次讲透,以后不管是做业务开发还是应对面试,都能少踩坑。

为啥多线程创建方式不能随便选?

在 Java 开发里,多线程几乎是绕不开的 —— 从处理异步任务(比如发送短信、生成报表),到应对高并发(比如秒杀接口、订单处理),都得靠多线程提高效率。但很多人刚开始学的时候,觉得 “能创建线程就行”,随便选一种写法就上手,却忽略了不同创建方式背后的设计逻辑和适用场景。

举个例子:如果你的需求是 “简单执行一个无返回值的异步任务”,用继承 类的方式确实快;但如果需要 “多个线程共享同一个任务逻辑”,比如多线程处理同一份文件的不同片段java多线程程序,那实现 接口才是更合理的选择 —— 要是这时候还硬用 类,不仅要重复写任务逻辑,还容易出现共享资源竞争的问题。

更关键的是,Java 里不同的多线程创建方式,还会影响后续的扩展性:比如继承 类后,就没法再继承其他类了(Java 单继承限制);而用接口实现的方式,后续还能灵活扩展其他功能。所以选对创建方式,不只是 “能不能跑通” 的问题,更是 “代码好不好维护、能不能应对复杂场景” 的关键。

从基础到进阶:5 种多线程创建方式,附代码示例和适用场景

接下来咱们逐个拆解 Java 里常用的 5 种多线程创建方式,每种都给上代码示例和明确的适用场景,你可以根据自己的需求直接套用。

1. 基础方式 1:继承 类

这是最直观的创建方式,只要新建一个类继承 ,重写 run () 方法,把要执行的任务写在 run () 里,然后调用 start () 方法就能启动线程。

代码示例:

// 1. 继承Thread类
class MyThread extends Thread {
    // 2. 重写run()方法,定义线程要执行的任务
    @Override
    public void run() {
        // 这里写具体的任务逻辑,比如打印线程名称和执行内容
        System.out.println("当前线程:" + Thread.currentThread().getName() + ",执行任务:处理简单异步逻辑");
    }
}
// 3. 调用start()启动线程
public class ThreadDemo {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        thread1.setName("任务线程1");
        thread1.start(); // 启动线程,会自动调用run()方法
        
        MyThread thread2 = new MyThread();
        thread2.setName("任务线程2");
        thread2.start();
    }
}

适用场景:适合简单的、无共享逻辑的单任务场景,比如单个异步通知、简单的日志打印。

注意点:因为 Java 是单继承,所以如果你的类已经继承了其他类(比如 类),就不能再用这种方式了;而且多个线程之间无法共享任务逻辑,每个线程都要新建一个子类实例。

2. 基础方式 2:实现 接口

这种方式是新建一个类实现 接口,同样重写 run () 方法,然后把这个 实例传给 类的构造方法,再调用 start () 启动。

代码示例:

// 1. 实现Runnable接口
class MyRunnable implements Runnable {
    // 2. 重写run()方法,定义共享任务逻辑
    @Override
    public void run() {
        // 多个线程会共享这个run()里的逻辑
        System.out.println("当前线程:" + Thread.currentThread().getName() + ",执行共享任务:处理文件片段");
    }
}
// 3. 把Runnable实例传给Thread,启动线程
public class RunnableDemo {
    public static void main(String[] args) {
        // 创建一个共享的Runnable实例
        MyRunnable sharedRunnable = new MyRunnable();
        
        // 多个线程共用同一个Runnable实例,共享任务逻辑
        Thread threadA = new Thread(sharedRunnable, "文件处理线程A");
        Thread threadB = new Thread(sharedRunnable, "文件处理线程B");
        
        threadA.start();
        threadB.start();
    }
}

适用场景:适合多个线程需要共享任务逻辑的场景,比如多线程处理同一份数据、分布式任务拆分;而且因为是实现接口,不影响类的继承关系,扩展性更强。

注意点:如果 实例里有共享变量,要注意线程安全(比如加 锁),避免数据错乱。

3. 基础方式 3:实现 接口(带返回值)

前面两种方式的 run () 方法都没有返回值java多线程程序,也没法抛出受检异常 —— 如果你的任务需要 “执行完返回结果”(比如计算一个复杂的数值、获取远程接口的返回值)Java 开发必看!多线程创建 3 种核心方式 + 2 种进阶方案讲透,那实现 接口就最合适了。

需要重写 call () 方法,这个方法可以返回泛型结果,还能抛出异常;不过 不能直接传给 ,得借助 类(它实现了 接口)来包装,最后再传给 启动。

代码示例:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
// 1. 实现Callable接口,指定返回值类型(这里是Integer)
class MyCallable implements Callable {
    private int taskId;
    
    // 可以通过构造方法传参,比如任务ID
    public MyCallable(int taskId) {
        this.taskId = taskId;
    }
    

java多线程程序

// 2. 重写call()方法,带返回值和异常抛出 @Override public Integer call() throws Exception { System.out.println("当前线程:" + Thread.currentThread().getName() + ",执行计算任务" + taskId); // 模拟任务执行耗时(比如复杂计算) Thread.sleep(1000); // 返回计算结果(比如任务ID*10) return taskId * 10; } } // 3. 用FutureTask包装Callable,启动线程并获取结果 public class CallableDemo { public static void main(String[] args) throws Exception { // 创建Callable实例 MyCallable callable1 = new MyCallable(1); MyCallable callable2 = new MyCallable(2); // 用FutureTask包装Callable,FutureTask兼具Runnable和Future的功能 FutureTask futureTask1 = new FutureTask(callable1); FutureTask futureTask2 = new FutureTask(callable2); // 启动线程 new Thread(futureTask1, "计算线程1").start(); new Thread(futureTask2, "计算线程2").start(); // 调用get()方法获取返回值(会阻塞,直到任务执行完成) Integer result1 = futureTask1.get(); Integer result2 = futureTask2.get(); System.out.println("计算任务1结果:" + result1); // 输出10 System.out.println("计算任务2结果:" + result2); // 输出20 } }

适用场景:需要获取线程执行结果的场景,比如异步计算、远程接口调用、多任务结果汇总(比如统计多个数据源的数据后合并)。

注意点: 的 get () 方法会阻塞当前线程,直到任务完成,所以如果不需要立即获取结果,可以先做其他逻辑,最后再调用 get ();另外如果任务可能耗时很久,建议设置超时时间(get (long , unit)),避免一直阻塞。

4. 进阶方式 1:线程池()

前面三种基础方式,每次创建线程都要 new (),但线程的创建和销毁是有开销的 —— 如果频繁创建线程(比如秒杀场景每秒几百个请求),会浪费大量资源,甚至导致线程数过多引发 OOM。

这时候就需要线程池了:线程池会预先创建一批线程,任务来了直接用空闲线程执行,任务结束后线程不销毁,放回池里复用。Java 里通过 框架实现线程池,常用的创建方式是 工具类,或者自定义 。

代码示例(用 创建固定大小线程池):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建固定大小的线程池(比如3个核心线程)
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        
        // 2. 提交任务(可以提交Runnable或Callable)
        for (int i = 1; i  {
                System.out.println("线程池线程:" + Thread.currentThread().getName() + ",执行任务" + taskId);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 3. 关闭线程池(先停止接收新任务,等已提交任务执行完再关闭)
        threadPool.shutdown();
    }
}

适用场景:高并发、频繁创建线程的场景,比如接口请求处理、批量任务执行(比如定时同步数据)、秒杀系统;几乎所有生产环境的多线程场景,都推荐用线程池Java 开发必看!多线程创建 3 种核心方式 + 2 种进阶方案讲透,而不是手动 new 。

注意点: 工具类创建的线程池有默认参数,比如 可能创建过多线程导致 OOM,所以生产环境建议自定义 ,根据业务需求设置核心线程数、最大线程数、队列容量等参数;另外线程池一定要记得关闭,避免资源泄漏。

5. 进阶方式 2:Fork/Join 框架(并行计算)

如果你的任务可以拆分成多个 “子任务”,子任务执行完后再合并结果(比如计算 1 到 10000 的总和,可以拆成 10 个线程分别计算 1-1000、1001-2000…… 最后把结果加起来),那 Fork/Join 框架就很适合 —— 它是 Java 7 引入的,专门用于 “分而治之” 的并行计算场景。

Fork/Join 的核心是 类,通常我们会继承它的子类 (有返回值)或 (无返回值),重写 () 方法实现任务拆分和合并。

代码示例(用 Fork/Join 计算 1 到 n 的总和):

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
// 1. 继承RecursiveTask(有返回值),指定返回值类型为Long
class SumTask extends RecursiveTask {
    // 拆分阈值:当任务规模小于等于这个值时,直接计算,不拆分
    private static final int THRESHOLD = 1000;
    private long start;
    private long end;
    

java多线程程序

public SumTask(long start, long end) { this.start = start; this.end = end; } // 2. 重写compute()方法,实现任务拆分和合并 @Override protected Long compute() { long sum = 0; // 判断任务是否需要拆分:如果规模小于阈值,直接计算 if (end - start <= THRESHOLD) { for (long i = start; i <= end; i++) { sum += i; } } else { // 任务拆分:分成两个子任务 long mid = (start + end) / 2; SumTask leftTask = new SumTask(start, mid); SumTask rightTask = new SumTask(mid + 1, end); // 执行子任务(fork()方法会异步执行子任务) leftTask.fork(); rightTask.fork(); // 获取子任务结果并合并(join()方法会阻塞直到子任务完成) long leftResult = leftTask.join(); long rightResult = rightTask.join(); sum = leftResult + rightResult; } return sum; } } // 3. 用ForkJoinPool执行任务 public class ForkJoinDemo { public static void main(String[] args) { // 创建ForkJoinPool ForkJoinPool forkJoinPool = new ForkJoinPool(); // 创建任务:计算1到10000的总和 SumTask sumTask = new SumTask(1, 10000); // 提交任务并获取结果 Long totalSum = forkJoinPool.invoke(sumTask); System.out.println("1到10000的总和:" + totalSum); // 输出50005000 // 关闭线程池 forkJoinPool.shutdown(); } }

适用场景:大规模并行计算场景,比如大数据量的统计分析(比如计算海量日志的指标)、复杂算法的并行实现(比如归并排序)、文件批量处理(比如遍历文件夹下所有文件并统计大小)。

注意点:任务拆分的阈值很关键 —— 阈值太大可能导致并行度不够,阈值太小会增加任务拆分和合并的开销,需要根据业务场景测试调整;另外 Fork/Join 框架会利用 CPU 的多核特性,适合 CPU 密集型任务,不适合 IO 密集型任务(IO 密集型用线程池更合适)。

总结:3 分钟快速选对多线程创建方式

最后咱们用一张表把 5 种方式的核心区别和适用场景汇总一下,以后遇到需求就能直接对号入座:

创建方式

核心特点

适用场景

注意事项

继承 类

单继承、无共享逻辑

简单异步任务、无返回值

不能继承其他类,避免共享变量

实现 接口

多实现、可共享逻辑

多线程共享任务、无返回值

共享变量需保证线程安全

实现 接口

带返回值、可抛异常

需获取任务结果(如异步计算)

.get () 会阻塞,建议设超时

线程池()

线程复用、高并发支持

频繁创建线程、IO 密集型任务(如接口处理)

生产环境建议自定义线程池参数

Fork/Join 框架

分而治之、并行计算

大规模 CPU 密集型任务(如大数据统计)

合理设置任务拆分阈值,避免过度拆分

其实 Java 多线程创建方式没有 “最好”,只有 “最合适”—— 比如做一个简单的日志异步打印,用 或 就够了;但如果是秒杀系统的订单处理,必须用线程池;如果是计算海量数据的指标,Fork/Join 才是最优解。

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » Java 开发必看!多线程创建 3 种核心方式 + 2 种进阶方案讲透

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们