在JS中,各种回调函数漫天飞,写的代码基本都是异步的,需要靠某个event去trigger。比如点击事件,就是最常见的。但是Java里面做异步就得绕好几弯,还不一定行,需要各种判断和校验,生怕出错。
异步执行的好处是显而易见的,比如某个接口需要查好几张表,调很多方法,其中有几个方法是非常耗时的。但是很多代码的做法还是同步的,传统的顺序执行。优点是可读性强,不容易出错。
假如A方法耗时特别长的话,那么整合接口的返回就会很慢。
比如,现在有一个服务,非常耗时。
class AccountService {
//耗时6秒的任务
List<String> queryAccountInfo(String accountNo) throws InterruptedException {
ArrayList<String> accountList = new ArrayList<String>();
accountList.add("A账户--" + accountNo);
accountList.add("账户状态:正常");
accountList.add("账户额度:8500");
Thread.sleep(3000);
return accountList;
}
}
对于这种耗时任务,当然是希望他可以单独去跑,他在跑的时候,别影响我去做别的事情。
最简单的思路就是用一个线程,可是这样就有一个问题,啥时候任务完成呢?所以,就挺烦的。还好JDK给我们提供了一个很好的类--FutureTask。
从字面意义上看,他是一个未来任务,构造方法如下:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable){
if (callable ==null)
throw new NullPointerException();
this.callable =callable;
this.state=NEW; // ensure visibility of callable
}
他接收一个callable对象,还有一个构造器重载:
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future<?> f = new FutureTask<Void>(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
接收Runnable对象。
嗯,按照一贯的套路,如果是自己设计,肯定会弄一个Runnable属性,可是FutureTask偏偏没有这么做,而是用了适配器模式:
this.callable = Executors.callable(runnable, result);
就是强行把Runnabale对象转换为callable对象。
因为Callable有返回值但是Runnable没有,所以就给一个假的result。换句话说,这个result是你自己传进去的,等任务执行完毕后,再还给你。
下面是一个测试Demo,总体来看,确实可以优化接口的返回时间,代价是需要增加好几个类。
class WrongArgumentException extends Exception {
public WrongArgumentException(String message){
super(message);
}
}
class AccountService {
//耗时6秒的任务
List<String> queryAccountInfo(String accountNo)throws InterruptedException{
ArrayList<String> accountList=new ArrayList<String>();
accountList.add("A账户--" +accountNo);
accountList.add("账户状态:正常");
accountList.add("账户额度:8500");
Thread.sleep(3000);
return accountList;
}
}
class AccountQueryService extends AccountService implements Callable{
private String accountNo;
public AccountQueryService(String accountNo){
this.accountNo=accountNo;
}
@Override
public Object call()throws Exception{
if(accountNo==null){
throw new WrongArgumentException("参数传递错误:accountNo为空!");
}
return queryAccountInfo(accountNo);
}
}
public class TestFutureTask {
public static void main(String[]args)throws InterruptedException,ExecutionException{
long start=System.currentTimeMillis();
//创建线程池
ExecutorService newSingleThreadExecutor =Executors.newSingleThreadExecutor();
Map<String, Object> result=new java.util.HashMap<String, Object>();
//执行耗时操作:查询账户信息 ,直接返回结果,不过现在为空
Future<List> task=newSingleThreadExecutor.submit(new AccountQueryService("5201314"));
//不影响主线程,异步线程自己在执行
Thread.sleep(1000);
result.put("cardInfo","主卡信息:奥特曼星球联名卡");
//等待异步线程是否结束
int timeout=5000; //接口超时时间5
while(!task.isDone()){
if(System.currentTimeMillis()-start>timeout){
System.out.println("任务超时,提前结束!");
result.put("code","-1");
result.put("msg","task time out!");
System.out.println(result);
return;
}
Thread.sleep(100);
//System.out.println("账户查询任务尚未结束,请耐心等到1秒...");
}
//获取异步任务的结果
result.put("accountInfo",task.get());
result.put("code","00000");
newSingleThreadExecutor.shutdown(); //关闭线程池
System.out.println(result);
System.out.println("任务总耗时:" +(System.currentTimeMillis()-start)/1000 +"秒");
}
}
运行结果:
{accountInfo=[A账户--5201314, 账户状态:正常, 账户额度:8500], cardInfo=主卡信息:奥特曼星球联名卡, code=00000}
任务总耗时:3秒
虽然代码还很粗糙,不过效果还是有的。对于那种耗时特别久,而且是多任务的情况,胆子够肥的话可以试试。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)