如何理解 guava 中的 MoreExecutors#directExecutor ?

guava 是谷歌官方一个著名的 Java 类库,增加了很多 feature ,很多都被官方借鉴。我在使用 guava 提供的 future 的时候,发现了 MoreExecutor#directExecutor 这个方法。对这个方法的理解产生了一些疑问。

场景是这样的,我们在使用 SettableFuture/ListenableFuture 的时候,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FutureExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
SettableFuture<Integer> future0 = SettableFuture.create();
// 使用其他线程去 set 对应的结果。
executor.submit(() -> {
future0.set(1);
});

Futures.addCallback(future0, new FutureCallback<>() {
@Override
public void onSuccess(Integer result) {
// 这一行会被哪个线程执行?主线程?还是上面的线程池?
System.out.println("result=" + result);
}

@Override
public void onFailure(Throwable t) {
}
}, MoreExecutors.directExecutor());
}
}

执行 callback 的线程池我们这里指定为 MoreExecutors#directExecutor ,那么这里执行打印 result 的线程到底是哪个线程呢?

查看执行线程

可以看到是主线程。

关于 MoreExecutors#directExecutor ,可以看到定义是这样的:

1
2
3
4
5
6
public final class MoreExecutors {
// 省略了类内其他成员
public static Executor directExecutor() {
return DirectExecutor.INSTANCE;
}
}

以及

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GwtCompatible
@ElementTypesAreNonnullByDefault
enum DirectExecutor implements Executor {
INSTANCE;

@Override
public void execute(Runnable command) {
command.run();
}

@Override
public String toString() {
return "MoreExecutors.directExecutor()";
}
}

MoreExecutors#directExecutor 其实是一个假的线程池,表示直接执行。

那么是由谁直接执行呢?在最上面的例子中可以看到是由主线程直接执行的,那么再看下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FutureExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
SettableFuture<Integer> future0 = SettableFuture.create();
// 使用其他线程去 set 对应的结果。
executor.submit(() -> {
// 增加线程 sleep 的逻辑。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future0.set(1);
});

Futures.addCallback(future0, new FutureCallback<>() {
@Override
public void onSuccess(Integer result) {
// 这一行会被哪个线程执行?主线程?还是上面的线程池?
System.out.println("result=" + result);
}

@Override
public void onFailure(Throwable t) {
}
}, MoreExecutors.directExecutor());
}
}

再看是谁执行的:

查看执行线程

换成上面指定的线程池,那么这里清晰了:

  • 如果 future 已经完成,那么 MoreExecutor#directExecutor 表示当前线程;
  • 如果 future 未完成,那么 MoreExecutor#directExecutor 就是未来完成 future 的线程。

因此其实具体执行回调的线程某种程度上是不确定的。

在较低版本的 guava 中,像 Futures#addCallback 这类函数是可以不用指线程池的,当然默认行为就是使用 MoreExecutors#directExecutor 。高版本的 guava 已经去掉不带线程池的接口了,更加突出线程池在这里的地位,也就是希望开发者更加明确这里的线程池的使用,MoreExectors#directExecutor 某种程度上是模糊的。