Java并发-带返回值的线程池处理流程分析

示例提供

以这段代码为例, 下文讲述带返回值的线程池处理流程。

// 新建一个线程池
private ExecutorService executor = new ThreadPoolExecutor(20, 50, 2, TimeUnit.SECONDS,
      new LinkedBlockingQueue<Runnable>(50), new ThreadPoolExecutor.CallerRunsPolicy());
Future<Boolean> future = executor.submit(new Callable<Boolean>() {
			public Boolean call() throws Exception {
                // ...
				return true;
			};
		});
// 获取结果
Boolean res = future.get(timeout, TimeUnit.MILLISECONDS);

先说结论

**返回值存储在FutureTask类的内部变量outcome 中,设置返回值由FutureTask#run方法处理,获取返回值由FutureTask#get方法处理 **。

下面是FutureTask类一些重要的变量以及构造函数

/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;

/**
 * 参数为callable
 */ 
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

/**
 * 参数为runnable
 */ 
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

相关UML图

image-20240124171139935

设值流程

由ThreadPoolExecutor#submit开始提交线程

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

由上面的构造函数可以看出此处参数也可以是runnable, 会通过Executors.callable(runnable, result)进行组装成callable。

再执行ThreadPoolExecutor#execute方法, 最后返回future对象。

那么看execute方法

public void execute(Runnable command) {
    // ...
    // 添加一个 worker来执行任务
    addWorker(command, true))
    // ...
}

private boolean addWorker(Runnable firstTask, boolean core) {
    // ...
    // 新建一个工作任务 执行
    Worker w = new Worker(firstTask);
    final Thread t = w.thread;
    t.start();
    // ...
}

Worker执行操作

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    
    // ...
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }

}

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // task != null,直接执行该task
        // task == null,getTask() 从工作队列中获取task
        while (task != null || (task = getTask()) != null) {
            w.lock();
            
            try {
                // 钩子函数,任务执行前的操作
                beforeExecute(wt, task);
                try {
                    // 任务执行
                    task.run();
                } catch (RuntimeException x) {
                    ...
                } finally {
                    // 钩子函数,任务执行后的操作
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        //...
    }
}

任务执行, 最终走到了FutureTask#run。 前面的细节流程可以看之前的一篇文章分析Java并发-线程池源码分析

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     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 {
        // ...
    }
}


protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        // 将运行结果设值到属性outcome上
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        // 处理线程唤醒等操作
        finishCompletion();
    }
}

可以看到最终在Future#set方法处完成了异步结果的赋值。

取值流程

取值的流程相对简单, 直接Future#get方法获取。

/**
 * 不带阻塞时间
 */
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    // 异步操作未处理完成时, 进行阻塞等待
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    // 返回outcome 或者异常
    return report(s); 
}

/**
 * 带阻塞时间
 */
public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    // 异步操作未处理完成时, 进行阻塞等待
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    // 返回outcome 或者异常
    return report(s);
}

【本文完】

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 很高兴能够回答您的问题!Java线程池的使用例子是:首先,你需要创建一个ExecutorService对象,它可以创建线程池,然后你可以向线程池提交任务,这些任务会被线程池分配给线程去执行,最后,你可以调用shutdown()来关闭线程池。例如:ExecutorService executorService = Executors.newFixedThreadPool(5);// 创建一个线程池,有5个线程 executorService.execute(new RunnableTask());// 使用线程池执行任务 executorService.shutdown();// 关闭线程池 ### 回答2: Java线程池是一种管理和复用线程的机制,它提供了一种高效地执行多个并发任务的方式。 下面是一个使用Java线程池的简单例子: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小为5的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交10个任务给线程池执行 for (int i = 0; i < 10; i++) { Runnable task = new Task(i); executor.submit(task); } // 关闭线程池 executor.shutdown(); } private static class Task implements Runnable { private int taskId; public Task(int taskId) { this.taskId = taskId; } @Override public void run() { System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName()); } } } ``` 在这个例子中,我们首先使用`Executors.newFixedThreadPool(5)`方法创建了一个固定大小为5的线程池。然后,我们通过`executor.submit(task)`方法提交了10个任务给线程池执行。 每个任务实际上是一个实现了`Runnable`接口的`Task`类的实例。在任务的`run`方法中,我们简单地打印任务的编号以及当前执行任务的线程名称。 最后,我们使用`executor.shutdown()`方法关闭了线程池,这样线程池就不会再接受新任务了。 在运行以上代码后,你会发现这10个任务在5个线程上有序地执行,每个线程负责执行多个任务,这样就提高了执行效率。 这只是使用Java线程池的一个简单示例,实际应用中,线程池可以用于更复杂的并发任务的执行,可以控制线程的数量、监控任务的执行状态,还可以获取任务的执行结果等。 ### 回答3: Java线程池是用来管理线程的一个重要工具,它可以使得我们更加有效地利用系统资源以及提高线程的执行效率。下面是一个使用Java线程池的示例: 假设我们有一个需求,需要同时处理多个任务,例如计算1到100之间的所有整数的平方和、计算1到100之间的所有整数的立方和以及计算1到100之间的所有整数的阶乘和。 我们可以创建一个实现了Callable接口的任务类,例如SquareSumTask、CubeSumTask和FactorialSumTask,分别用来计算平方和、立方和和阶乘和。这些任务类需要实现call方法,用来实现具体的计算逻辑。 接下来,我们可以使用线程池来管理这些任务的执行。我们可以通过使用Executors类中的newFixedThreadPool方法来创建一个固定大小的线程池,例如创建一个有3个线程的线程池。然后,我们可以将任务提交给线程池进行执行,例如通过调用submit方法,并传入需要执行的任务。 最后,我们可以通过Future类的get方法来获取任务的执行结果,并进行处理。通过get方法可以获取任务执行的返回值,如果任务还没有执行完毕,get方法会阻塞当前线程直到任务执行完毕并返回结果。 在这个例子中,通过使用线程池,我们可以同时处理多个任务,提高了程序的效率。线程池会根据任务的数量和线程池的大小来动态管理线程的创建和销毁,可以有效地避免线程创建和销毁的开销。 总之,Java线程池是一个非常实用的工具,通过合理地使用线程池可以提高程序的并发性能和资源利用率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值