前言
本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和参考文献请见100个问题搞定Java并发
正文
1 .特殊的 DirectExecutor 线程池
在 MoreExecutors 中,提供了一个简单但是非常重要的线程池实现,即 DirectExecutor 线程池。
DirectExecutor 线程池很简单,它并没有真的创建或者使用额外线程,它总是将任务在当前线程中直接执行。
也许你会觉得很奇怪,为什么需要这么一个线程池呢?
这是软件设计
上的需要。
从软件设计的角度上说,抽象是软件设计的根本和精髓。
将不同业务的共同属性提取并抽象成模型非常有利于对不同业务的统一处理。
我们总是希望并且倾向于使用通用的代码来处理不同的场景,因此,这就需要对不同场景进行统一的抽象和建模。
对于线程池来说,其技术目的是为了复用线程以提高运行效率,但其业务需求却是去异步执行一段业务指令。
但是有时候,异步并不是必要的。
因此,当我们剥去线程池的技术细节,仅关注其使用场景时便不难发现,任何一个可以运行 Runnable 实例的模块都可以被视为线程池,即便它没有真正创建线程。
这样就可以将异步执行和同步执行进行统一,使用统一的编码风格来处理同步和异步调用,进而简化设计。
举例
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.Executor;
public class MoreExecutorsDemo {
public static void main(String[] args) {
Executor executor = MoreExecutors.directExecutor();
executor.execute(() -> System.out.println("I am running in " + Thread.currentThread().getName()));
}
}
I am running in main
可以看到,这个 Runnable 接口在主线程中执行。
注入不同的 executor 的实现,例如使用固定大小线程池替代 DirectExecutor ,无须修改代码便可以使程序拥有不同的行为,这也正是 DirectExecutor 的用意所在。
2 . Daemon 线程池
此外,在 MoreExecutors 中,还提供了将普通线程池转为 Daemon 线程池
的方法。
在很多场合,我们并不希望后台线程池阻止程序的退出,当系统执行完成后,即便有线程池存在,依然希望进程结束执行。
此时,就可以使用 MoreExecutors.getExitingExecutorService()
方法。
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class MoreExecutorsDemo2 {
public static void main(String[] args) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
MoreExecutors.getExitingExecutorService(executor);
executor.execute(() -> System.out.println("I am running in " + Thread.currentThread().getName()));
}
}
I am running in pool-1-thread-1
上述代码输出后,立即退出程序,若不使用 MoreExecutors.getExitingExecutorService() 方法对 executor 线程池进行设置,则该程序无法正常退出,除非手动关闭 executor 线程池。
3 . 对 Future模式的扩展
在 JDK 自带的简单 Future 模式中,虽然我们可以使用 Future.get() 方法得到 Future 的处理结果,但是这个方法是阻塞的,因此并不利于我们开发高并发应用。
但在 Guava 中,增强了 Future 模式,增加了对 Future 模式完成时的回调接口,使得 Future 完成时可以自动通知应用程序进行后续处理。
举例
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.Executors;
public class FutureDemo {
public static void main(String[] args) throws InterruptedException {
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<String> task = service.submit(new RealData("x"));
task.addListener(() -> {
System.out.print("异步处理成功:");
try {
System.out.println(task.get());
} catch (Exception e) {
e.printStackTrace();
}
}, MoreExecutors.directExecutor());
System.out.println("main task done.....");
Thread.sleep(3000);
}
}
class RealData implements Callable<String> {
private String para;
public RealData(String para){
this.para=para;
}
@Override
public String call() throws Exception {
StringBuffer sb=new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(para);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
return sb.toString();
}
}
main task done.....
异步处理成功:xxxxxxxxxx