【多线程】FutureTask多线程底层实现

【多线程】FutureTask多线程底层实现

MyFutureTask最简版本实现思路

文章目录

1-DeepSeek版本-MyFutureTask

要实现 MyFutureTask<V>,需要理解它作为 RunnableFuture 的双重身份:既是 Runnable(可被线程执行),又是 Future(提供异步结果获取)。最小实现的核心是状态管理等待/通知机制,下面给出设计思路和最小代码。


设计思路

  1. 任务持有
    • 通过 Callable<V> 封装实际任务(也可通过 Runnable + result 构造)。
    • 若使用 Runnable,可通过 Executors.callable(runnable, result) 转换为 Callable
  2. 执行状态
    • 使用 volatile boolean done 标记是否完成(正常/异常/取消均视为完成)。
    • 使用 volatile boolean cancelled 标记是否被取消。
    • 保存执行结果 V result 或异常 Throwable exception
  3. 线程安全
    • 所有状态变更和条件等待均通过 synchronized(this) 保护。
    • 使用 wait()/notifyAll() 实现 get() 的阻塞等待。
  4. run() 方法
    • 执行前检查是否已完成,避免重复执行。
    • 记录当前执行线程 runner(用于支持中断,最小实现可保留)。
    • 调用 callable.call(),正常完成则设置 result,异常则设置 exception
    • 无论结果如何,最后标记 done = true 并唤醒所有等待线程。
  5. cancel() 方法
    • 若已完成则返回 false
    • 否则标记 cancelled = truedone = true(阻止后续执行)。
    • mayInterruptIfRunningtruerunner 非空,则中断该线程。
    • 唤醒等待线程。
  6. get() 方法
    • synchronized 块中循环检查 done,未完成则 wait()
    • 完成后检查 cancelledexception,分别抛出 CancellationExceptionExecutionException
    • 否则返回 result
  7. 超时 get()
    • 最小实现可暂不实现,或使用 wait(long timeout) 简单处理。

最小版本代码实现

java 复制代码
import java.util.concurrent.*;

public class MyFutureTask<V> implements RunnableFuture<V> {
    private final Callable<V> callable;
    private V result;
    private Throwable exception;
    private volatile boolean done;
    private volatile boolean cancelled;
    private Thread runner; // 用于支持中断

    // 构造:通过 Callable
    public MyFutureTask(Callable<V> callable) {
        this.callable = callable;
    }

    // 构造:通过 Runnable + 结果
    public MyFutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
    }

    @Override
    public void run() {
        // 双重检查,防止已取消或已完成的任务被执行
        if (done) return;
        synchronized (this) {
            if (done) return;
            runner = Thread.currentThread();
        }

        try {
            V v = callable.call();
            synchronized (this) {
                result = v;
                done = true;
                notifyAll(); // 唤醒等待 get() 的线程
            }
        } catch (Throwable t) {
            synchronized (this) {
                exception = t;
                done = true;
                notifyAll();
            }
        } finally {
            runner = null;
        }
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        if (done) return false;
        synchronized (this) {
            if (done) return false;
            cancelled = true;
            done = true; // 标记完成,阻止后续执行
            if (mayInterruptIfRunning && runner != null) {
                runner.interrupt();
            }
            notifyAll();
            return true;
        }
    }

    @Override
    public boolean isCancelled() {
        return cancelled;
    }

    @Override
    public boolean isDone() {
        return done;
    }

    @Override
    public V get() throws InterruptedException, ExecutionException {
        synchronized (this) {
            while (!done) {
                wait(); // 等待任务完成
            }
        }
        if (cancelled) {
            throw new CancellationException();
        }
        if (exception != null) {
            throw new ExecutionException(exception);
        }
        return result;
    }

    // 最小版本暂不实现超时 get,可留空或简单实现
    @Override
    public V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        // 如需支持可参考如下:
        // long nanos = unit.toNanos(timeout);
        // synchronized (this) {
        //     while (!done) {
        //         if (nanos <= 0) throw new TimeoutException();
        //         nanos = wait(nanos); // 但 wait(long) 以毫秒计,建议使用 LockSupport
        //     }
        // }
        // ... 同上的后续处理
        throw new UnsupportedOperationException("超时 get 暂未实现");
    }
}

关键点说明

  • 状态原子性donecancelledvolatile 保证可见性,但复合操作(如检查+修改)仍需 synchronized
  • 中断支持runner 记录当前执行线程,cancel 可尝试中断,但任务本身需响应中断(本例中 Callable 需自行处理中断)。
  • 超时版本 :可基于 wait(long)LockSupport 实现,但为保持最小,此处仅提供骨架。
  • 异常处理 :执行期间抛出的任何 Throwable 都会被封装为 ExecutionException 抛出。

这个最小实现涵盖了 Future 的核心契约,可满足简单的异步任务需求,实际生产环境建议直接使用 java.util.concurrent.FutureTask


2-Kimi版本-MyFutureTask

复制代码
public class MyFutureTask<V> implements RunnableFuture<V> {}

假如我想写一个简单的MyFutureTask实现,我的代码编写思路应该是什么,给我一个最小版本的代码实现

实现 MyFutureTask 的核心思路是:它既是 Runnable(能被线程执行),又是 Future(能拿到异步结果)。所以你需要同时搞定「任务执行」和「结果阻塞等待」两件事。

最小版本的关键设计点:

要解决的问题 最小实现方案
状态管理 volatile int state(0=NEW, 1=RUNNING, 2=DONE, 3=CANCELLED)
阻塞等待结果 synchronized + wait()/notifyAll()
取消任务 记录执行线程 runner,调用 runner.interrupt()
异常包装 ExecutionException 包装业务异常

下面是一个可直接拷贝运行的最小版本:

java 复制代码
import java.util.concurrent.*;

public class MyFutureTask<V> implements RunnableFuture<V> {
    
    // 状态常量
    private static final int NEW       = 0;
    private static final int RUNNING   = 1;
    private static final int DONE      = 2;
    private static final int CANCELLED = 3;
    
    private volatile int state = NEW;
    private Callable<V> callable;
    private V result;
    private Throwable exception;
    private Thread runner;  // 记录实际执行 run() 的线程,用于 cancel 时中断
    
    public MyFutureTask(Callable<V> callable) {
        if (callable == null) throw new NullPointerException();
        this.callable = callable;
    }
    
    // ========== Runnable 接口 ==========
    @Override
    public void run() {
        // 如果已经不是 NEW 状态,说明已经有人在跑了,直接返回
        if (!casState(NEW, RUNNING)) return;
        
        runner = Thread.currentThread();
        try {
            V r = callable.call();
            setResult(r);
        } catch (Throwable t) {
            setException(t);
        }
    }
    
    // ========== Future 接口 ==========
    @Override
    public V get() throws InterruptedException, ExecutionException {
        synchronized (this) {
            // 没完成就等着
            while (state < DONE) {
                wait();
            }
            return report();
        }
    }
    
    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        long nanos = unit.toNanos(timeout);
        synchronized (this) {
            if (state < DONE) {
                if (nanos <= 0) throw new TimeoutException();
                wait(nanos / 1_000_000, (int) (nanos % 1_000_000));
                if (state < DONE) throw new TimeoutException();
            }
            return report();
        }
    }
    
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        synchronized (this) {
            if (state >= DONE) return false;  // 已经跑完了,取消无效
            
            state = CANCELLED;
            if (mayInterruptIfRunning && runner != null) {
                runner.interrupt();
            }
            notifyAll();  // 唤醒所有在 get() 里 wait 的线程
            return true;
        }
    }
    
    @Override
    public boolean isCancelled() {
        return state == CANCELLED;
    }
    
    @Override
    public boolean isDone() {
        return state >= DONE;
    }
    
    // ========== 内部辅助方法 ==========
    
    /** 非阻塞地尝试更新状态(简单版 CAS,用 synchronized 保证原子性) */
    private boolean casState(int expect, int update) {
        synchronized (this) {
            if (state != expect) return false;
            state = update;
            return true;
        }
    }
    
    /** 正常结束,设置结果 */
    private void setResult(V v) {
        synchronized (this) {
            if (state == RUNNING) {
                result = v;
                state = DONE;
                notifyAll();
            }
        }
    }
    
    /** 异常结束,设置异常 */
    private void setException(Throwable t) {
        synchronized (this) {
            if (state == RUNNING) {
                exception = t;
                state = DONE;
                notifyAll();
            }
        }
    }
    
    /** 根据最终状态返回结果或抛出异常 */
    private V report() throws ExecutionException {
        if (state == CANCELLED) throw new CancellationException();
        if (exception != null) throw new ExecutionException(exception);
        return result;
    }
    
    // ========== 演示 ==========
    public static void main(String[] args) throws Exception {
        // 1. 创建一个 Callable 任务
        Callable<Integer> task = () -> {
            System.out.println("[" + Thread.currentThread().getName() + "] 开始计算...");
            Thread.sleep(1000);
            return 42;
        };
        
        // 2. 包装成 MyFutureTask
        MyFutureTask<Integer> futureTask = new MyFutureTask<>(task);
        
        // 3. 丢给线程执行(因为它实现了 Runnable)
        new Thread(futureTask, "Worker-1").start();
        
        // 4. 主线程阻塞等待结果(因为它实现了 Future)
        System.out.println("[" + Thread.currentThread().getName() + "] 等待结果...");
        Integer result = futureTask.get();
        System.out.println("结果: " + result);
        
        // --- 取消演示 ---
        MyFutureTask<Integer> futureTask2 = new MyFutureTask<>(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                // 忙等,直到被中断
            }
            return 99;
        });
        new Thread(futureTask2).start();
        Thread.sleep(50);
        boolean cancelled = futureTask2.cancel(true);
        System.out.println("取消成功? " + cancelled);
        try {
            futureTask2.get();
        } catch (CancellationException e) {
            System.out.println("确实被取消了,抛出了 CancellationException");
        }
    }
}

代码编写思路总结:

  1. 构造时存下 Callable :这是真正要执行的逻辑。JDK 源码里还会处理 Runnable + result 的情况,最小版只保留 Callable 即可。
  2. run() 里做状态竞争casState(NEW, RUNNING) 保证同一个任务不会被多个线程重复执行。
  3. get()wait() 阻塞 :没完成就挂起,完成后 notifyAll() 唤醒。这是 JDK 源码的核心骨架(源码里用更复杂的 LockSupport.park/unpark,但 wait/notify 足够演示原理)。
  4. cancel() 中断执行线程 :记录 runner 引用,调用 interrupt(),让 Callable.call() 里的阻塞操作(如 sleep)能感知到中断。
  5. 异常统一包成 ExecutionException :这是 Future.get() 的契约------业务异常不会直接抛,而是包一层。

相关推荐
XR12345678814 分钟前
办公组网像“看病“:先问诊,再选型
开发语言·php
weixin1997010801614 分钟前
[特殊字符]《从0到1:闲鱼开放平台授权登录 + AccessToken 刷新 + 聚石塔部署完整链路》(附Python源码)
java·数据库·python
布莱克60516 分钟前
strcpy 函数详解:作用、用法与安全缺陷
c语言·开发语言·c++·安全
软件黑马王子25 分钟前
3.单例模式问题1:构造函数
开发语言·单例模式·c#
Freak嵌入式32 分钟前
RP2040 PIO 编程模型与状态机原理:从硬件架构到工作逻辑全解析
java·大数据·开发语言·单片机·嵌入式硬件·硬件架构
cmes_love37 分钟前
国内期货Level2五档行情与逐笔成交数据介绍
数据库·区块链
kakawzw38 分钟前
Netty源码笔记
java·服务器·后端
zhuodedao42 分钟前
Spring AI + MCP 文件工具未调用问题复盘:为什么初始化成功却没有写入文件?
java·debug·agent·springai·mcp
cmes_love1 小时前
CME和CBOT外盘期货数据下载资源介绍
数据库·区块链