ThreadPoolExecutor中的submit()方法详细讲解

本文深入剖析了ThreadPoolExecutor中的submit()方法,包括其参数类型、内部实现机制及FutureTask对象的作用,通过实例演示了如何使用submit()并获取任务执行结果。

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

ThreadPoolExecutor中的submit()方法详细讲解

在使用线程池的时候,发现除了execute()方法可以执行任务外,还发现有一个方法submit()可以执行任务。

submit()有3个参数不一的方法,这些方法都是在ExecutorService接口中声明的,在AbstractExecutorService中实现,而ThreadPoolExecutor继承AbstractExecutorService

<T> Future<T> submit(Callable<T> callable);

<T> Future<T> submit(Runnable var1, T result);

Future<?> submit(Runnable runnable);

我们可以看到submit()的参数既可以是Runnable,又可以是Callable。对于Runnable我们是比较熟的,它是线程Thread所执行的任务,里面有一个run()方法,是任务的具体执行操作。那么Callable呢?我们一起看下他们的代码吧。

public interface Runnable {
    void run();
}


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

Runnable这里就不介绍了,Callable接口定义了一个call()方法,返回一个Callable指定的泛型类,并且call()调用的时候会抛出异常。通过比较RunnableCallable还看不什么端倪,那么我们就看看内部实现吧。

  • submmit()参数解析

这里重点分析submit()带参数RunnableCallable的方法

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

我们发现2者的实现没有任何的差异,唯一就是submit()参数不同。

参数传入newTaskFor()方法,那么可以肯定就是在这个方法里做了什么操作。

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}


protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}

newTaskFor()的目的就是创建一个FutureTask对象,那我们追踪到FutureTask的构造方法(FutureTask非常关键,后面会分析)。

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       
}


public FutureTask(Callable<V> callable) {
    if (callable == null)throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       
}

到了这里我们知道,其实Runnable会在这里转化成Callable。我们来看下Executors.callable()具体实现。

public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}


private static final class RunnableAdapter<T> implements Callable<T> {
    private final Runnable task;
    private final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

Executors.callable()创建了一个RunnableAdapter对象,RunnableAdapter实现了Callable接口,在call()方法中调用了传入的Runnablerun(),并且将传入的result参数返回。

也就是说我们调用submit()传入的Runnbale最终会转化成Callable,并且返回一个result值(如果我们传入这个参数则返回这个参数,不传入则返回null)。

到这里我们讲清楚了submit()的参数的区别和内部实现,submit()方法有一个返回值Future,下面我们来分析一下返回值Future

  • submit()的返回值Future

上面分析submit()源码可知,submit()返回的是一个RunnableFuture类对象,真正是通过newTaskFor()方法返回一个new FutureTask()对象。所以submit()返回的真正的对象是FutureTask对象。

那么FutureTask是什么,我们来看下它的类继承关系。

public class FutureTask<V> implements RunnableFuture<V> {
    ...
}

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

通过继承关系我们可以明确的知道其实FutureTask就是一个Runnable。并且有自己run()实现。我们来看下FutureTaskrun()是如何实现的。

public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

我们在new FutureTask()对象的时候,在FutureTask构造方法中会对state状态赋值为NEW,并且传入一个callable对象。通过FutureTaskrun()我们可以知道,其实就通过state状态判断,调用callable的call()。(如果传入的参数是RunnableRunnableRunnableAdapter类中转化时,在call()中,其实调用的就是Runnablerun()方法)。

所以在submit()方法中,调用了一个execute(task)的方法,实际执行的是FutureTaskrun(),而FutureTaskrun()调用的是Callablecall()方法。

说了这么多,submit()最后执行的还是传入的Runnablerun()Callablecall()方法。好像没有FutureTask什么事啊。

其实不是,submit()返回FutureTask对象,通过这个FutureTask对象调用get()可以返回submit()方法传入的一个泛型类参数result对象,如果是Callable直接通过call()返回。这个返回值的可以用来校验任务执行是否成功。

  • FutureTask的get()的实现
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);   //等待任务执行完
    return report(s);//将执行的任务结果返回
}


private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)
        return (V)x;
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

最后是通过outcome参数将根据任务的状态将结果返回。那么outcome参数在哪里赋值了?outcome参数赋值的地方有好2处,一是FutureTaskset(),二是FutureTasksetException()

set()是在FutureTaskrun()执行完成后,将传入的result参数赋值给传入给set(),赋值给outcome参数。如果run()报异常了会将Throwable对象通过setException()方法传入,赋值给outcome变量

大家可以返回上面的run()查看下。

protected void set(V v) {
    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
        outcome = v;
        U.putOrderedInt(this, STATE, NORMAL); // final state
        finishCompletion();
    }
}


protected void setException(Throwable t) {
    if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
        outcome = t;
        U.putOrderedInt(this, STATE, EXCEPTIONAL); // final state
        finishCompletion();
    }
}
  • submit()使用案例
public class Test {
    private static final String SUCCESS = "success";

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(3);

        System.out.println("------------------任务开始执行---------------------");

        Future<String> future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                System.out.println("submit方法执行任务完成" + "   thread name: " + Thread.currentThread().getName());
                return SUCCESS;
            }
        });

        try {
            String s = future.get();
            if (SUCCESS.equals(s)) {
                String name = Thread.currentThread().getName();
                System.out.println("经过返回值比较,submit方法执行任务成功    thread name: " + name);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("-------------------main thread end---------------------");
    }
}

打印结果:

------------------任务开始执行---------------------
call()调用开始: 1496899867882
submit方法执行任务完成: 1496899872897   thread name: pool-1-thread-1
经过返回值比较,submit方法执行任务成功    thread name: main
-------------------main thread end---------------------

主线程会一直阻塞,等待线程池中的任务执行完后,在执行后面的语句。

<think>好的,用户想了解Python中多线程和ThreadPoolExecutor的使用方法及最佳实践。我需要先整理ThreadPoolExecutor的基本用法,然后给出示例代码,接着讨论最佳实践,比如如何处理共享资源、避免GIL的影响,以及线程池的配置。用户提供的引用里提到线程安全、竞态条件、GIL限制,这些都需要在回答中涵盖。首先,我应该从基本示例入手,展示如何创建线程池、提交任务。然后解释参数如max_workers的作用。接下来,讨论异常处理和资源管理,比如使用with语句。然后,针对共享数据的问题,引入锁机制,引用中的例子提到字典的竞态条件,可以演示如何用Lock来解决。此外,引用4提到GIL的影响,需要说明多线程适合I/O密集型任务,而不是计算密集型。最后,总结最佳实践,如线程池大小设置、避免共享状态、使用队列等。还要注意回答结构清晰,分步骤讲解,确保用户能逐步理解和应用。检查是否有遗漏点,比如future对象的使用、回调函数,或者结果获取方法。可能还需要提到调试多线程程序的技巧,比如日志记录。确保示例代码正确,符合Python语法,并且注释清楚。最后生成相关问题,比如如何选择线程数,处理异常的方法,或者与其他并发模块的比较。需要确保引用正确,比如引用3提到共享字典的问题,所以在讲竞态条件时要标注引用。现在组织内容,先介绍ThreadPoolExecutor,再示例,然后最佳实践,最后注意事项和常见问题。</think>### Python多线程编程与ThreadPoolExecutor实践指南 #### 一、ThreadPoolExecutor基础使用 1. **线程池创建与任务提交** ```python from concurrent.futures import ThreadPoolExecutor import time def task(n): print(f"执行任务{n}") time.sleep(2) return n**2 # 创建含4个工作线程的线程池 with ThreadPoolExecutor(max_workers=4) as executor: # 提交单个任务 future = executor.submit(task, 5) print(f"任务结果: {future.result()}") # 批量提交任务 results = executor.map(task, [1,2,3,4]) print(f"批量结果: {list(results)}") ``` *参数说明*:`max_workers`推荐设置为`min(32, os.cpu_count() + 4)`[^4] 2. **异步回调机制** ```python def callback(future): print(f"回调收到结果: {future.result()}") with ThreadPoolExecutor() as executor: future = executor.submit(task, 10) future.add_done_callback(callback) ``` #### 二、最佳实践要点 1. **资源管理规范** - 始终使用`with`语句确保线程池正确关闭 - 单个任务应封装为独立函数 - 任务函数需包含超时重试机制 2. **线程安全实现** ```python from threading import Lock shared_dict = {} lock = Lock() def safe_write(key, value): with lock: # 使用锁保证原子操作[^3] shared_dict[key] = value ``` 3. **异常处理模板** ```python def wrapped_task(n): try: return task(n) except Exception as e: print(f"任务{n}异常: {str(e)}") return None with ThreadPoolExecutor() as executor: futures = [executor.submit(wrapped_task, i) for i in range(5)] ``` #### 三、性能优化策略 1. **任务类型适配** - I/O密集型:推荐多线程(网络请求/文件操作) - CPU密集型:建议改用`ProcessPoolExecutor` 2. **动态参数调整** ```python import os optimal_workers = min(32, os.cpu_count() * 5 + 4) ``` 3. **结果处理模式** ```python # 实时处理完成的任务 with ThreadPoolExecutor() as executor: futures = {executor.submit(task, i): i for i in range(10)} for future in as_completed(futures): orig_arg = futures[future] result = future.result() print(f"任务{orig_arg}返回{result}") ``` #### 四、常见问题解决方案 1. **竞态条件预防** - 使用`Queue`代替直接共享变量 - 优先选择线程安全数据结构 - 对共享资源访问必须加锁[^3] 2. **调试技巧** - 使用`threading.current_thread().name`跟踪线程 - 通过`sys.settrace`设置线程跟踪函数 - 日志记录添加线程标识 3. **GIL应对方案** - 混合使用多进程+多线程模型 - 关键计算使用C扩展 - 考虑asyncio协程方案 $$ T_{optimal} = \min(32, C_{cpu} \times K + B) $$ 其中$C_{cpu}$为CPU核心数,$K$为任务类型系数(I/O密集取5,计算密集取1),$B$为缓冲常数(通常取4)[^4]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值