非阻塞线程编程之美
程序员文章站
2022-03-08 22:35:58
...
背景
今天年后最后一天了,是不是很羡慕?不要羡慕,虽然是最后一天了,但是我依然在code的路上,只不过没有在写业务代码了,而是在写一些自己感兴趣的东西,前几天翻了翻多线程的东西,感觉还是又有新的收获的,于是今天又抽空看了下。
线程阻塞
我们都知道多线程可以提高系统处理业务的能力,但是我们有没有考虑过,很多时候我们编写的多线程代码是阻塞的,尤其是主线程大部分需要阻塞,比如我们常用的join,还有future的get方法,当然很业务场景下,确实是需要阻塞的,比如上篇文章的最后一个demo
java多线程浅析之sleep、wait、join详解
但是!!!总有些场景主线程想要去干点别的事情的,今天就以泡茶案例来分析下非阻塞线程的编程之美。
案例分析:
- 烧开水
- 洗杯子
- 泡茶、喝茶
烧水线程
public class HotWaterThread implements Callable<Boolean> {
@Override
public Boolean call() {
System.out.println("烧水中。。。。。。");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
}
洗杯子线程
public class WashCupThread implements Callable<Boolean> {
@Override
public Boolean call(){
System.out.println("洗杯子中。。。。。。");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
}
测试线程泡茶喝水
@Test
public void test2() throws ExecutionException, InterruptedException {
FutureTask<Boolean> hotTask = new FutureTask<>(new HotWaterThread());
Thread hotWater = new Thread(hotTask);
FutureTask<Boolean> washTask = new FutureTask<>(new WashCupThread());
Thread washCup = new Thread(washTask);
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(hotWater);
pool.submit(washCup);
Boolean h = hotTask.get();
Boolean w = washTask.get();
while (!(h && w)){
System.out.println("看书咯。。。。。");
}
System.out.println("泡茶了。。。。。");
System.out.println("喝茶了。。。。。");
}
运行结果
很显然没有读书,问题出在哪呢?问题出在get方法那里,get方法是一个阻塞方法,直到线程结束,所以在执行烧水和洗杯子线程的时候,主线程是被阻塞的。
Guava异步回调
Guava是google公司提供的对java的扩展,Guava就实现了非阻塞异步回调获取future的功能,Guava的实现主要依赖以下两个接口:
- ListenableFuture 继承了java的Future接口,使得异步结果能被监控
- FutureCallback 新的接口,根据异步结果的不同,完成不同的回调处理
Guava实现泡茶并读书案例
public class GuavaTest {
private static Boolean hW = false;
private static Boolean wC = false;
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 创建烧水线程
Callable<Boolean> hotWater = new HotWaterThread();
// 创建洗杯子线程
Callable<Boolean> washCup = new WashCupThread();
// 构建java线程池
ExecutorService jpool = Executors.newFixedThreadPool(2);
// 构建guava线程
ListeningExecutorService gpool = MoreExecutors.listeningDecorator(jpool);
// 提交烧水线程
ListenableFuture<Boolean> hotWaterFuture = gpool.submit(hotWater);
// 烧水线程绑定异步回调
Futures.addCallback(hotWaterFuture, new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
hW = true;
System.out.println("烧水成功");
}
@Override
public void onFailure(Throwable throwable) {
hW = false;
System.out.println("烧水失败");
}
});
// 提交洗杯子线程
ListenableFuture<Boolean> washCupFuture = gpool.submit(washCup);
Futures.addCallback(washCupFuture, new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean aBoolean) {
wC = true;
System.out.println("洗杯子成功");
}
@Override
public void onFailure(Throwable throwable) {
wC = false;
System.out.println("洗杯子失败");
}
});
while (!(hW && wC)){
System.out.println("看书咯。。。。。。");
}
System.out.println("泡茶了。。。。。");
System.out.println("喝茶了。。。。。");
System.out.println(System.currentTimeMillis()- start + "毫秒");
}
}
运行结果:
读书成功,因为烧水和洗杯子实现了真正的异步,就好比请了个佣人负责烧水和洗杯子,虽然仍然需要花费时间,但是我的精力已经不用关注烧水时水是否烧干的问题了。
总结
本文主要演示了阻塞线程和非阻塞线程的案例。
扫码关注个人公众号
下一篇: 利用ASP存取各种常用类型数据库(3)