杨斌
发布于 2026-04-15 / 5 阅读
0
0

Java如何实现多线程

Java 中实现多线程主要有 4 种常见方式,从简单到灵活依次是:继承 Thread、实现 Runnable、实现 Callable(可返回结果)、以及使用线程池。下面用代码案例逐一说明

image-dBTr.png

一、继承 Thread 类(简单但不推荐)

java

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 运行中");
    }
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
    }
}

缺点:单继承限制,任务与线程耦合。


二、实现 Runnable 接口(推荐解耦)

java

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 执行Runnable");
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        Thread t2 = new Thread(new MyRunnable());
        t1.start();
        t2.start();
    }
}

Lambda 简化(Java 8+):

java

Thread t = new Thread(() -> System.out.println("Lambda线程"));
t.start();

三、实现 Callable + FutureTask(带返回值)

java

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return Thread.currentThread().getName() +"任务结果";
    }
    public static void main(String[] args) throws Exception {
        FutureTask<String> task1 = new FutureTask<>(new MyCallable());
        FutureTask<String> task2 = new FutureTask<>(new MyCallable());
        new Thread(task1).start();
        new Thread(task2).start();
        System.out.println(task1.get()); // 阻塞获取结果
        System.out.println(task2.get()); // 阻塞获取结果
    }
}

四、线程池(核心:使用 ThreadPoolExecutor

❌ 不推荐:Executors 的隐患示例

java

// 危险:无界队列,任务多时 OOM
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
    pool.submit(() -> { 
        Thread.sleep(1000); 
    });
}

✅ 推荐:ThreadPoolExecutor 自定义参数

java

import java.util.concurrent.*;

public class SafeThreadPool {
    public static void main(String[] args) {
        // 核心线程数 = 2,最大线程数 = 5,空闲存活时间 = 60秒
        // 阻塞队列 = 有界队列 ArrayBlockingQueue 容量 10
        // 拒绝策略 = CallerRunsPolicy(让提交任务的线程自己执行)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,                               // corePoolSize
            5,                               // maximumPoolSize
            60L,                             // keepAliveTime
            TimeUnit.SECONDS,                // 时间单位
            new ArrayBlockingQueue<>(10),    // 有界队列,避免内存爆炸
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
        );

        // 提交任务
        for (int i = 1; i <= 20; i++) {
            final int taskId = i;
            executor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
                try { Thread.sleep(100); } catch (InterruptedException e) {}
            });
        }

        executor.shutdown();
    }
}

关键参数说明

参数

作用

防止 OOM(内存溢出)的关键

corePoolSize

常驻线程数

-

maximumPoolSize

最大线程数

限制线程数量

workQueue

阻塞队列

用有界队列(如 ArrayBlockingQueue),拒绝无限堆积

RejectedExecutionHandler

拒绝策略

当队列满且线程数达最大时,执行拒绝策略(如抛异常、阻塞调用者、丢弃等)

常用拒绝策略

  • AbortPolicy:直接抛 RejectedExecutionException(默认)

  • CallerRunsPolicy:由提交任务的线程自己执行,降低提交速度

  • DiscardPolicy:静默丢弃

  • DiscardOldestPolicy:丢弃队列中最老的任务

有界队列示例(防止内存爆炸)

java

// 有界队列,容量 100
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
// 或者用 LinkedBlockingQueue 指定容量
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);

五、CompletableFuture(异步编排,推荐现代用法)

内部使用 ForkJoinPool.commonPool(),生产环境建议自定义线程池(也是 ThreadPoolExecutor)。

java

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {
    public static void main(String[] args) {
        // 自定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50)
        );
        CompletableFuture.supplyAsync(() -> "Hello", executor)
                         .thenApply(s -> s + " World")
                         .thenAccept(System.out::println);
        executor.shutdown();
    }
}

CompletableFuture 就是“异步任务的流水线工具”:你可以提交一个任务,然后像搭积木一样串接后续步骤、组合多个并行任务,并且全程不阻塞当前线程。

总结对比(含安全建议)

方式

优点

缺点

何时使用

Thread

简单

单继承,资源浪费

极少使用

Runnable

解耦,灵活

无返回值

简单异步任务

Callable

有返回值

FutureTask

需要计算结果

Executors 工厂类

代码简洁

内存爆炸风险

不推荐生产使用

ThreadPoolExecutor

完全可控,安全

参数较多

生产环境唯一推荐

CompletableFuture

异步编排,优雅

需自定义线程池

复杂异步流水线

一句话:永远不要在正式项目中使用 Executors.newFixedThreadPool() / newCachedThreadPool()一律用 ThreadPoolExecutor 构造方法,并指定有界队列 + 合理拒绝策略


评论