Java Runnable/Callable/Future/FutureTask/ExecutorService 关系解析

本文介绍Java中线程池的使用方法,包括不同类型的线程池创建、任务提交及异常处理等。同时,还介绍了如何使用Callable与Future获取有返回值的任务结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,我们知道 Runnable 接口是无返回值可执行代码的基础接口:

public interface Runnable {
    void run();
}

自 Java 1.8 起该接口的匿名类可以简化为:

Runnable r = () -> { };

该 lambda 表达式很好理解,无参无返回值。

Thread 类是线程的抽象(它也实现了 Runnable),我们可以直接通过创建 Thread 对象来操作线程:

new Thread(() -> { }).start();

但我们希望线程受到管理,所以我们使用 Executor

public interface Executor {
    void execute(Runnable command);
}

主要是使用继承自 ExecutorExecutorService 类,它有以下常用的默认实现:

// 创建足够多的线程,并且循环使用,注意这里线程数没有上限
Executors.newCachedThreadPool();

// 创建固定数量的线程,未执行的任务进入等待队列
Executors.newFixThreadPool(threadNum);

// 创建至少某个数量的线程,以执行定时任务
Executors.newScheduledThreadPool(minThreadNum);

让我们使用一番:

ExecutorService executorService = Executors.newFixThreadPool(8);
for (int i = 0; i < 1000; i++) {
    executorService.execute(() -> { });
}
executorService.shutdown(); // 不再接受新任务,已有任务仍会继续执行
try {
    while (!executorService.isTerminated()) {
        // 等待已有任务结束
        executorService.awaitTermination(10, TimeUnit.SECONDS);
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

很好用。

异常不能跨线程传播,所以只能在线程本地处理。我们可以通过给 ExecutorService 指定 ThreadFactory,在创建线程时设置一些属性:

public interface ThreadFactory {
    Thread newThread(Runnable r);
}

Thread.UncaughtExceptionHandler 接口:

public interface UncaughtExceptionHandler {
    void uncaughtException(Thread t, Throwable e);
}
executorService = Executors.newSingleThreadExecutor(runnable -> {
    Thread t = new Thread(runnable);
    t.setUncaughtExceptionHandler((thread, exception) -> { });
    return t;
});

通过 ExecutorService 的帮助,我们能够使用 Runnable 的有返回值版本 Callable

public interface Callable<V> {
    V call() throws Exception;
}

public interface ExecutorService extends Executor {
    <T> Future<T> submit(Callable<T> task);
}
Callable<String> callable = () -> { return ""; };
Future<String> future = executorService.submit(callable);

// 若目标任务未结束,则当前线程将等待直到目标任务返回或被中断
String result = future.get(); // is ""

// 对于 Runnable 任务,返回值为 null
Future<?> future1 = executorService.submit(() -> {});
Object result1 = future1.get(); // is null

Java 1.6 起提供 RunnableFuture 表示可以被 Thread 执行,还可以通过 Future 接口访问其结果。

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

RunnableFuture 有一个实现 FutureTask

Callable<String> task = () -> { return ""; };
FutureTask<String> ft = new FutureTask<String>(task);

// 通过以下任一方式执行
new Thread(ft).start();
executorService.submit(ft);

// 当前线程等待结果后返回
ft.get();

另外,可以通过以下方法获取当前 CPU 数量,以更好地使用线程池:

int cpuNums = Runtime.getRuntime().availableProcessors();

转载于:https://my.oschina.net/tridays/blog/858758

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值