Guava---AbstractFuture

本文通过手写Future类的方式引入了回调的概念,并对比了自定义Future与AbstractFuture的区别。AbstractFuture能够避免回调函数执行时的阻塞问题,通过线程池异步执行回调任务,确保主线程的流畅运行。

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

在开始介绍AbstractFuture之前先让我们手动实现一个回调.

回调

手写Future类

public class Future<T> {
    private Consumer<T> consumer;

    public void addListener(Consumer<T> consumer) {
        this.consumer = consumer;
    }

    public void set(T value) {
        consumer.accept(value);
    }
}

测试回调

public static void main(String[] args) throws InterruptedException {
        Future<String> future = new Future();

        //监听future设置了值
        future.addListener(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("---------"+s);
            }
        });

        TimeUnit.SECONDS.sleep(5);
        future.set("hh");

        TimeUnit.SECONDS.sleep(1);
    }

执行mian方法,可以发现睡眠5秒后,future设置了值,这时addListener方法可以监听到future设置了值.
以上回调有个问题,假设Consumer接口中的方法执行需要大量的时间,那这样future.set(“hh”)的时候就会出现阻塞,其实对于这种情况,我们完全可以启用一个线程去实现,但是AbstractFuture已经为我们很好的解决方法.


AbstractFuture使用

先通过一个例子,感受下AbstractFuture的好处

 static class AbstractFutureImpl<T> extends AbstractFuture<T> {
        public boolean set(T value) {
            return super.set(value);
        }

    }

    // 创建线程池
    final static ExecutorService executors = Executors.newCachedThreadPool();

    public static void main(String[] args) throws InterruptedException {
        AbstractFutureImpl<String> future = new AbstractFutureImpl();
        //监听future设置了值
        future.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("进入回调函数");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println("收到set的值: " + future.get());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, executors);

        TimeUnit.SECONDS.sleep(5);
        future.set("hh");
        System.out.println("set完值");
        TimeUnit.SECONDS.sleep(100);
    }
    /**
     set完值
     进入回调函数
     收到set的值: hh
     */

由打印结果可以看出,future.set(“hh”)方法未阻塞,回调函数完全由传入线程池去执行. 当set完值时,就会主动回调addListener方法,通过future.get()(这时已经有值,故get()方法不会阻塞)获取到值.


AbstractFuture解析

AbstractFuture实现了ListenableFuture,Future接口,实现了接口中所有方法.无需我们重复造轮子.
那addListener是如何实现的呢?具体实现细节看源码,这里说下大概思路.

addListener核心源码:

public void add(Runnable runnable, Executor executor) {
    // Fail fast on a null.  We throw NPE here because the contract of
    // Executor states that it throws NPE on null listener, so we propagate
    // that contract up into the add method as well.
    Preconditions.checkNotNull(runnable, "Runnable was null.");
    Preconditions.checkNotNull(executor, "Executor was null.");

    // Lock while we check state.  We must maintain the lock while adding the
    // new pair so that another thread can't run the list out from under us.
    // We only add to the list if we have not yet started execution.
    synchronized (this) {
      if (!executed) {
        runnables = new RunnableExecutorPair(runnable, executor, runnables);
        return;
      }
    }
    // Execute the runnable immediately. Because of scheduling this may end up
    // getting called before some of the previously added runnables, but we're
    // OK with that.  If we want to change the contract to guarantee ordering
    // among runnables we'd have to modify the logic here to allow it.
    executeListener(runnable, executor);
  }

源码里有个executed判断,如何走if()判断,会把runnable和executor存储起来,否则直接通过executor执行runnable方法.
为什么会有executed呢? 看set(T value)方法源码就会知道,:
1. 如果addListener方法比set方法先执行,这时executed就是false, 执行set方法时会主动通过executor执行runnable方法.
2. 如果addListener方法比set方法后执行,这时executed就是ture.直接通过executor执行runnable方法.

set核心源码:

public void execute() {
    // Lock while we update our state so the add method above will finish adding
    // any listeners before we start to run them.
    RunnableExecutorPair list;
    synchronized (this) {
      if (executed) {
        return;
      }
      executed = true;
      list = runnables;
      runnables = null;  // allow GC to free listeners even if this stays around for a while.
    }
    // If we succeeded then list holds all the runnables we to execute.  The pairs in the stack are
    // in the opposite order from how they were added so we need to reverse the list to fulfill our
    // contract.
    // This is somewhat annoying, but turns out to be very fast in practice.  Alternatively, we 
    // could drop the contract on the method that enforces this queue like behavior since depending
    // on it is likely to be a bug anyway.

    // N.B. All writes to the list and the next pointers must have happened before the above 
    // synchronized block, so we can iterate the list without the lock held here.
    RunnableExecutorPair reversedList = null;
    while (list != null) {
      RunnableExecutorPair tmp = list;
      list = list.next;
      tmp.next = reversedList;
      reversedList = tmp;
    }
    while (reversedList != null) {
      executeListener(reversedList.runnable, reversedList.executor);
      reversedList = reversedList.next;
    }
  }

注: 思想是灵魂,实现是形式

"mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate -DarchetypeArtifactId="maven-archetype-webapp" -DarchetypeGroupId="org.apache.maven.archetypes" -DarchetypeVersion="1.4" -DgroupId="com.example" -DartifactId="demo"" WARNING: A restricted method in java.lang.System has been called WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/C:/Program%20Files/Java/apache-maven-3.9.9-bin/apache-maven-3.9.9/lib/jansi-2.4.1.jar) WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module WARNING: Restricted methods will be blocked in a future release unless native access is enabled WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/C:/Program%20Files/Java/apache-maven-3.9.9-bin/apache-maven-3.9.9/lib/guava-33.2.1-jre.jar) WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release [INFO] Scanning for projects... [INFO] [INFO] ------------------< org.apache.maven:standalone-pom >------------------- [INFO] Building Maven Stub Project (No POM) 1 [INFO] --------------------------------[ pom ]--------------------------------- [INFO] [INFO] >>> archetype:3.1.2:generate (default-cli) > generate-sources @ standalone-pom >>> [INFO] [INFO] <<< archetype:3.1.2:generate (default-cli) < generate-sources @ standalone-pom <<< [INFO] [INFO] [INFO] --- archetype:3.1.2:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Interactive mode
05-12
WARNING: A restricted method in java.lang.System has been called WARNING: java.lang.System::load has been called by org.fusesource.jansi.internal.JansiLoader in an unnamed module (file:/D:/Environment/apache-maven-3.8.8/lib/jansi-2.4.0.jar) WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module WARNING: Restricted methods will be blocked in a future release unless native access is enabled WARNING: A terminally deprecated method in sun.misc.Unsafe has been called WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/D:/Environment/apache-maven-3.8.8/lib/guava-25.1-android.jar) WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release [INFO] Scanning for projects... [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.046 s [INFO] Finished at: 2025-06-13T09:32:08+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] No goals have been specified for this build. You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: validate, initialize, generate-sources, process-sources, generate-resources, process-resources, compile, process-classes, generate-test-sources, process-test-sources, generate-test-resources, process-test-resources, test-compile, process-test-classes, test, prepare-package, package, pre-integration-test, integration-test, post-integration-test, verify, install, deploy, pre-clean, clean, post-clean, pre-site, site, post-site, site-deploy. -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoGoalSpecifiedException
最新发布
06-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值