CompletableFuture 源码分析(todo)

说明

  1. 本文基于 jdk 8 编写。
  2. @author JellyfishMIX - github / blog.jellyfishmix.com
  3. LICENSE GPL-2.0

asyncPool(自带的线程池)

请见注释:

	/**
     * Default executor -- ForkJoinPool.commonPool() unless it cannot
     * support parallelism.
     *
     * 自带的线程池,来自于 ForkJoinPool 的 commonPool。
     * ForkJoinPool 的 commonPool 在 static {} 里初始化,用了 Unsafe 这个闭源类,会根据机器的硬件配置去初始化 commonPool
     * 业务一般不用自带的线程池,会用业务自己传入的线程池。ForkJoinPool 的 commonPool 只是作为框架提供的默认实现,不推荐使用。
     */
    private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

thenApply(同步), thenApplyAsync(异步)

thenApply 方法同步地执行一个函数,thenApplyAsync 异步地执行一个函数(异步即不会占用当前线程,会使用其他线程)。

	public <U> CompletableFuture<U> thenApply(
        Function<? super T,? extends U> fn) {
        return uniApplyStage(null, fn);
    }

    public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn) {
        return uniApplyStage(asyncPool, fn);
    }

    public <U> CompletableFuture<U> thenApplyAsync(
        Function<? super T,? extends U> fn, Executor executor) {
        return uniApplyStage(screenExecutor(executor), fn);
    }

可以看到,不管是 thenApply 还是 thenApplyAsync 都调用了 uniApplyStage 方法,uniApplyStage 方法如下:

	private <V> CompletableFuture<V> uniApplyStage(
        Executor e, Function<? super T,? extends V> f) {
        // 针对传入的函数判空校验
        if (f == null) throw new NullPointerException();
        // 将要返回的 CompletableFuture 实例
        CompletableFuture<V> d =  new CompletableFuture<V>();
        // Async 直接进入 if 分支,sync 则执行返回值的 uniApply 尝试获取结果(因为 Async 会传入线程池,通过判断入参有无线程池,来判断是否异步)
        if (e != null || !d.uniApply(this, f, null)) {
            UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
            push(c);
            c.tryFire(SYNC);
        }
        return d;
    }

uniApplyStage 方法及其调用的方法,由于我的水平不足,暂未有能力解析,日后将完善。记录下质量较高的学习文章,分析时可供参考:深入解读CompletableFuture源码与原理 - CoderBruis - CSDN

  1. 如果调用方不依赖响应结果,dubbo 接口可以使用异步处理的方式,加快响应速度,避免让调用方长时间等待。例如以下写法:

    	public void callback(String requestId, SearchResponse searchResponse) {
            CompletableFuture.runAsync(() -> {...}, CALL_BACK_EXECUTOR);
        }
    
    1. 这种写法常见于 dubbo 回调方法中,因为触发回调方法的那方,不依赖于回调方法的执行结果,只是去做触发的动作。
    2. 此外,如果调用 provider 的 rpc 方法,consumer 可以选择不等待本次响应结果的返回,而是把一个回调方法当作入参传递给 provider,此时 provider 也可以使用异步处理的方式,来加快响应速度,避免 consumer 长时间等待。
  2. CompletableFuture 的两个 api 可以执行异步任务,supplyAsync (有返回值),runAsync (无返回值)。注意需要传入一个自定义的线程池。

  3. CompletableFuture 的 get() 和 getNow(T valueIfAbsent) 方法的区别:get() 会阻塞住,等待 CompletableFuture 有了 result 后才会返回。getNow(T valueIfAbsent) 方法不会阻塞,如果 CompletableFuture 还没有 result,则会把入参 valueIfAbsent 返回。

并行计算的组合写法。

大集合切分成多个小集合,使用 CompletableFuture.supplyAsync 多线程并行计算提高效率,最后用 CompletableFuture.allOf 收束,多线程转单线程(异步转同步)处理计算结果。

学习了 thenApply 和 thenCompose 的区别。

  1. thenApply 接收一个函数作为参数,使用此函数处理上一个 CompletableFuture 计算步骤的结果,此函数的返回结果作为本次计算步骤的 result。
  2. thenCompose 接收一个函数作为参数,此函数需返回 CompletableFuture 实例。此函数的入参是先前 CompletableFuture 计算步骤的结果。
  3. thenApply 的入参函数转换的是 result 泛型的类型,返回的是计算结果 result。最终 thenApply 返回的还是先前的 CompletableFuture。
  4. thenCompose 的入参函数使用上一个计算步骤的 result,最终会生成一个新的 CompletableFuture 并返回。

对 CompletableFuture 的 api 讲解比较清楚的一篇文章 CompletableFuture中whenComplete()和thenApply() 区别? - 程序员子龙的回答 - 知乎
https://www.zhihu.com/question/433003386/answer/2295290317

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Context包是Go语言内置的一个标准库,主要用于多个Goroutine之间的上下文传递和控制。它提供了一种机制来传递取消信号、截止时间和一些其他的请求/值,这些请求/值可以跨越多个API边界和Goroutine传递,而不需要显式地传递。 以下是Context包的主要源码分析: 1. Context接口 Context接口定义了一个可取消的上下文,它包括了Deadline截止时间、Done通道和Value键值对数据。 ```go type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} } ``` 2. context.Background() Background函数返回一个空的Context,它没有任何值和截止时间,而且永远不会取消。它被广泛用于Main函数、初始化和测试中。 ```go func Background() Context { return background } var ( background = new(emptyCtx) ) type emptyCtx int func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() <-chan struct{} { return nil } func (*emptyCtx) Err() error { return nil } func (*emptyCtx) Value(key interface{}) interface{} { return nil } ``` 3. context.TODO() TODO函数返回一个非空的Context,它没有任何值和截止时间,而且永远不会取消。它被广泛用于暂时不确定上下文应该是什么的情况。 ```go func TODO() Context { return todo } var ( todo = new(emptyCtx) ) ``` 4. context.WithCancel() WithCancel函数返回一个带有CancelFunc的Context,当CancelFunc调用时,Context的Done通道将被关闭。这个函数可以用来取消长时间运行的操作。 ```go func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } } type cancelCtx struct { Context mu sync.Mutex // protects following fields done chan struct{} // created lazily, closed by first cancel call children map[canceler]struct{} // set to nil by the first cancel call err error // set to non-nil by the first cancel call } func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent} } func (c *cancelCtx) Done() <-chan struct{} { c.mu.Lock() if c.done == nil { c.done = make(chan struct{}) } d := c.done c.mu.Unlock() return d } func (c *cancelCtx) Err() error { c.mu.Lock() defer c.mu.Unlock() return c.err } func (c *cancelCtx) cancel(removeFromParent bool, err error) { if err == nil { panic("context: internal error: missing cancel error") } c.mu.Lock() if c.err != nil { c.mu.Unlock() return // already canceled } c.err = err if c.done == nil { c.done = closedchan } else { close(c.done) } for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err) } c.children = nil c.mu.Unlock() if removeFromParent { removeChild(c.Context, c) } } type canceler interface { cancel(removeFromParent bool, err error) } var closedchan = make(chan struct{}) func init() { close(closedchan) } ``` 5. context.WithDeadline() WithDeadline函数返回一个带有截止时间的Context,当截止时间到达或者调用CancelFunc时,Context的Done通道将被关闭。 ```go func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { // The current deadline is already sooner than the new one. return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: deadline, } propagateCancel(parent, c) d := c.deadline.Sub(time.Now()) if d <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(true, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(d, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) } } type timerCtx struct { cancelCtx deadline time.Time mu sync.Mutex // protects timer and err timer *time.Timer err error } func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { return c.deadline, true } func (c *timerCtx) cancel(removeFromParent bool, err error) { c.cancelCtx.cancel(false, err) // propagate the cancel first c.mu.Lock() if c.timer != nil { c.timer.Stop() c.timer = nil } c.err = err c.mu.Unlock() if removeFromParent { removeChild(c.cancelCtx.Context, c) } } ``` 6. context.WithTimeout() WithTimeout函数返回一个带有超时时间的Context,当超时时间到达或者调用CancelFunc时,Context的Done通道将被关闭。 ```go func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout)) } ``` 7. context.WithValue() WithValue函数返回一个带有键值对数据的Context,这个数据可以跨越多个API边界和Goroutine传递,而不需要显式地传递。 ```go func WithValue(parent Context, key, val interface{}) Context { if key == nil { panic("nil key") } if val == nil { panic("nil value") } return &valueCtx{parent, key, val} } type valueCtx struct { Context key, val interface{} } func (c *valueCtx) Value(key interface{}) interface{} { if c.key == key { return c.val } return c.Context.Value(key) } ``` 以上就是Context包的源码分析。Context包提供了一种简单而强大的机制来传递请求/值和取消信号,可以用于管理并发访问、超时控制和错误处理等场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JellyfishMIX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值