谷歌的 guava 工具包和 hutool 的工具包是目前比较全而且好用的java开发工具包可以避免我们重复造轮子,最近看了下Guava的ListenableFutrue可以在线程任务执行完成之后执行我们自己编写的回调函数,从而避免了get()方法的阻塞,感觉不错,记录一下。
本示例使用maven作为jar包管理工具,首先在pom文件中引入guava工具包,代码如下:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>
<version>xxx</version> </dependency>
PS: hutool 工具包引入示例:
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.2.0</version> </dependency>
首先讲一下简单的业务逻辑: 这是一个阳光明媚的早上,小明在学习编程,然后他想喝茶了。就跟管家说,你好Jenkins我想喝茶。然后就有两个仆人去做事儿了,一个人洗茶杯一个人烧水,小明继续学习。茶杯洗完了仆人会告诉小明然后把茶杯端过来,水烧开了仆人也告诉小明,把热水壶拎过来。小明暂停学习然后去快乐地泡茶喝茶。然而,仆人可能在工作的过程中出岔子,其结果也会告知小明,小明只能放弃喝茶。以下是实现代码:
1 package com.vincent.fat.core.juc; 2 3 import cn.hutool.core.util.RandomUtil; 4 import com.google.common.util.concurrent.*; 5 import lombok.extern.slf4j.Slf4j; 6 import org.checkerframework.checker.nullness.compatqual.NullableDecl; 7 import org.jetbrains.annotations.NotNull; 8 9 import java.util.concurrent.Callable; 10 import java.util.concurrent.ExecutorService; 11 import java.util.concurrent.Executors; 12 import java.util.concurrent.TimeUnit; 13 @Slf4j 14 public class GuavaFutureDemo { 15 public static void main(String[] args) { 16 Thread.currentThread().setName("主线程"); 17 ExecutorService executorService = Executors.newFixedThreadPool(10); 18 DrinkTeaJob drinkTeaJob=new DrinkTeaJob(executorService); 19 new Thread(drinkTeaJob,"喝茶线程").start(); 20 Callable<Boolean> hotWaterJob = new HotWaterJob(); 21 Callable<Boolean> cleanCapsJob = new CleanCapsJob(); 22 ListeningExecutorService gpool = MoreExecutors.listeningDecorator(executorService); 23 ListenableFuture<Boolean> hotFuture = gpool.submit(hotWaterJob); 24 ListenableFuture<Boolean> cleanWaterFuture = gpool.submit(cleanCapsJob); 25 Futures.addCallback(hotFuture, new FutureCallback<Boolean>() { 26 @Override 27 public void onSuccess(@NullableDecl Boolean result) { 28 if (result != null) { 29 drinkTeaJob.setWaterOk(result); 30 drinkTeaJob.setWaterJobDone(true); 31 } 32 } 33 @Override 34 public void onFailure(@NotNull Throwable t) { 35 drinkTeaJob.setWaterJobDone(true); 36 throw new RuntimeException(t.getMessage()); 37 } 38 },executorService); 39 Futures.addCallback(cleanWaterFuture, new FutureCallback<Boolean>() { 40 @Override 41 public void onSuccess(@NullableDecl Boolean result) { 42 if (result != null) { 43 drinkTeaJob.setCupsOk(result); 44 drinkTeaJob.setCupsJobDone(true); 45 } 46 } 47 @Override 48 public void onFailure(@NotNull Throwable t) { 49 drinkTeaJob.setCupsJobDone(true); 50 throw new RuntimeException(t.getMessage()); 51 } 52 },executorService); 53 log.warn("[{}]运行到最后一行代码",Thread.currentThread().getName()); 54 } 55 static class HotWaterJob implements Callable<Boolean>{ 56 /** 57 * Computes a result, or throws an exception if unable to do so. 58 * 59 * @return computed result 60 * @throws Exception if unable to compute a result 61 */ 62 @Override 63 public Boolean call() throws Exception { 64 TimeUnit.SECONDS.sleep(2); 65 if (RandomUtil.randomBoolean()){ 66 log.info("水已经烧好了..."); 67 return true; 68 } 69 else { 70 throw new RuntimeException("火灭了,水烧不开了。。。"); 71 } 72 } 73 } 74 static class CleanCapsJob implements Callable<Boolean>{ 75 /** 76 * Computes a result, or throws an exception if unable to do so. 77 * 78 * @return computed result 79 * @throws Exception if unable to compute a result 80 */ 81 @Override 82 public Boolean call() throws Exception { 83 TimeUnit.SECONDS.sleep(1); 84 boolean res; 85 if (res=RandomUtil.randomBoolean()){ 86 log.info("茶杯洗好了。。。。"); 87 } 88 else { 89 throw new RuntimeException("茶具摔坏了。。。"); 90 } 91 return true; 92 } 93 } 94 static class DrinkTeaJob implements Runnable{ 95 private boolean waterOk=false; 96 97 public boolean isWaterJobDone() { 98 return waterJobDone; 99 } 100 101 public void setWaterJobDone(boolean waterJobDone) { 102 this.waterJobDone = waterJobDone; 103 } 104 105 public boolean isCupsJobDone() { 106 return cupsJobDone; 107 } 108 109 public void setCupsJobDone(boolean cupsJobDone) { 110 this.cupsJobDone = cupsJobDone; 111 } 112 113 private boolean waterJobDone=false; 114 private boolean cupsJobDone=false; 115 private boolean cupsOk=false; 116 private final ExecutorService executorService; 117 DrinkTeaJob(ExecutorService executorService){ 118 this.executorService=executorService; 119 } 120 public void setWaterOk(boolean waterOk) { 121 this.waterOk = waterOk; 122 } 123 124 public void setCupsOk(boolean cupsOk) { 125 this.cupsOk = cupsOk; 126 } 127 128 private int gap=0; 129 /** 130 * When an object implementing interface <code>Runnable</code> is used 131 * to create a thread, starting the thread causes the object's 132 * <code>run</code> method to be called in that separately executing 133 * thread. 134 * <p> 135 * The general contract of the method <code>run</code> is that it may 136 * take any action whatsoever. 137 * 138 * @see Thread#run() 139 */ 140 @Override 141 public void run() { 142 while (!Thread.currentThread().isInterrupted()) 143 { 144 log.info("正在学习编程。。。"); 145 try{ 146 TimeUnit.MILLISECONDS.sleep(this.gap); 147 if (waterJobDone&&cupsJobDone) 148 { 149 this.drinkTea(); 150 } 151 } catch (InterruptedException interruptedException) { 152 log.error("喝茶线程被中断了",interruptedException); 153 break; 154 } 155 this.gap+=500; 156 } 157 log.info("喝茶线程退出!!"); 158 this.executorService.shutdown(); 159 } 160 161 private void drinkTea() throws InterruptedException { 162 if (this.cupsOk&&this.waterOk) 163 { 164 log.info("泡茶,喝茶,苏福~~~"); 165 }else { 166 log.warn("茶喝不成了,哭。。。。"); 167 } 168 TimeUnit.SECONDS.sleep(1); 169 Thread.currentThread().interrupt(); 170 } 171 } 172 }
程序运行结果1:
程序运行结果2: