电子商务网站建设系统,最新室内装修效果图大全,河南工程建设协会网站,黔江网站建设美团本地生活面试#xff1a;模拟外卖订单处理#xff0c;客户支付提交订单后#xff0c;查询订单详情#xff0c;后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。 时间好快#xff0c;一转眼不到一个月时间#xff0c;已经完成分享synchronized、volatile、… 美团本地生活面试模拟外卖订单处理客户支付提交订单后查询订单详情后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。 时间好快一转眼不到一个月时间已经完成分享synchronized、volatile、CAS、AQS、ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier、并发锁、Condition、线程池、ThreadLocal等多个核心基础原理和案例剖析。
其实我编写文章速度真的很慢基本每篇文章需要写2~3个小时去梳理。确保基础理论、源码分析、面试案例、优缺点等均分享到位力求每篇都是干货实用让每位看到我文章的同学不管是面试、还是应用到工作实践都能有所收益。
今天我们围绕Future是什么、怎么用实践demo来展开分享实现原理架构来展开。
一、Future是什么
首先我们回到一个问题就是为什么需要Future、FutureTask
之前我们用过的线程池ThreadPoolExecutor、线程Thread都可以执行异步任务但是无法执行带有返回值的异步任务。而Future是可以执行这种带有返回值的异步任务。线程池ThreadPoolExecutor、Thread线程可以通过提交执行Future类型的任务就可以获取任务返回值。
和Callable、Runnable一样Future是一个接口。我们看一下它的接口源码。
//Runnable接口只有一个run方法
FunctionalInterface
public interface Runnable {public abstract void run();
}
//Callable接口只有一个call方法
FunctionalInterface
public interface CallableV {V call() throws Exception;
} //本文主角Future有5个方法
public interface FutureV {boolean cancel(boolean mayInterruptIfRunning);boolean isCancelled();boolean isDone();V get() throws InterruptedException, ExecutionException;V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
}
代码非常少有5个方法但是核心方法是get(),还是isDone(); get()方法用来读取任务的返回结果。此外如果任务未执行调用该方法的当前线程会进入阻塞等待。
isDone()方法检查计算是否已完成。这个方法不会阻塞线程。日常使用Future一般是先调用isDone()方法判断结果是否返回然后再调用get()方法获取执行结果。
一句话总结Future是异步计算任务提交Future任务后可以继续干别的等干完别的事再回来通过get()方法去读取Future任务结果。
再简单总结Future是支持在未来读取结果的异步计算任务。
cancel()方法:用来尝试取消任务仅仅是尝试不一定成功。如果任务已经开始执行那么它就不能被取消。
isCancelled()方法:就是用来检查这个Future任务是否被取消。 Future和之前分享的信号量Semaphore、CountDownLatch倒数门闩、CyclicBarrier循环屏障都不一样唯一和Condition条件队列有点像支持多个线程协调进行。支持异步读取任务结果这个特性Future可以很方便支持多个任务并发执行以及在最后汇总获取并发结果最后返回给终端。 二、应用实践模拟同时查外卖信息
我们用FutureTask实现一个并发面试题模拟外卖订单处理客户支付提交订单后查询订单详情后台需要查询店铺备餐进度、以及外卖员目前位置信息后再返回。
package lading.java.mutithread;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** 模拟外卖订单处理客户支付提交订单后查询订单详情后台* 通过查询店铺备餐进度、以及外卖员目前位置信息。*/
public class Demo015Future {public static void main(String[] args) throws ExecutionException, InterruptedException {//1、异步查询商家系统任务FutureTaskBoolean checkFoodIsReadyTask new FutureTask(() - {System.out.println(Thread.currentThread().getName()转发请求到商家系统查询餐厅当前订单备餐进度...);Thread.sleep(2000);boolean foodIsOk true;System.out.println(Thread.currentThread().getName()商家接口返回是否备餐完成结果是 foodIsOk);return foodIsOk;});//2、异步查询外卖员系统任务FutureTaskBoolean checkCourierIsReadyTask new FutureTask(() - {System.out.println(Thread.currentThread().getName()转发请求到外卖员系统查询外卖员是否已到店...);Thread.sleep(800);boolean courierIsOk true;System.out.println(Thread.currentThread().getName()查询外卖员是否已到店结果是 courierIsOk);return courierIsOk;});//并发查询商家、外卖员情况大约2snew Thread(checkFoodIsReadyTask).start();new Thread(checkCourierIsReadyTask).start();Thread.sleep(100);//只是判断是否完成不会阻塞if (!checkFoodIsReadyTask.isDone()) {System.out.println(Thread.currentThread().getName() 查询店铺备餐进度未完成继续等等...);}if (!checkCourierIsReadyTask.isDone()) {System.out.println(Thread.currentThread().getName() 查询外卖员情况未完成继续等等...);}//如果结果没返回会阻塞等待System.out.println(Thread.currentThread().getName() 线程 成功查到商家备餐结果 checkFoodIsReadyTask.get());System.out.println(Thread.currentThread().getName() 线程 成功查到外卖员结果 checkCourierIsReadyTask.get());}
}
运行结果 三、硬核干活FutureTask的实现原理源码分析
我们从它的属性开始然后讲实现的方法原理。
3.1 FutureTask的属性
FutureTask 的源码也不多属性就这5个。
线程状态state、执行任务的callable,任务执行的返回结果outCome,正在运行的线程runner等待队列里的waiters节点。 3.1.1 当前任务线程状态state以及状态枚举
这七种任务状态之间相互转换关系
1、正常结束
NEW - COMPLETING - NORMAL
2、异常结束
NEW - COMPLETING - EXCEPTIONAL
3、任务被取消
NEW - CANCELLED
4、任务出现中断
NEW - INTERRUPTING - INTERRUPTED
//当前任务线程的状态以下几个枚举值都是state不同状态
private volatile int state;//当前任务线程刚创建状态
private static final int NEW 0;
//当前任务线程即将完成ing是一个即将完成状态
private static final int COMPLETING 1;
//表示当前任务线程正常结束的状态
private static final int NORMAL 2;
//表示当前任务线程有异常
private static final int EXCEPTIONAL 3;
//表示当前任务线程被取消
private static final int CANCELLED 4;
//表示当前线程已经被打了中断标识
private static final int INTERRUPTING 5;
//表示当前线程已经被中断
private static final int INTERRUPTED 6;
3.2 其他属性意义
//Callable 来执行任务Callable 有返回值的线程
private CallableV callable;//Callable任务执行的返回结果
private Object outcome; //当前正在运行的线程
private volatile Thread runner;//等待队列节点WaitNode是FutureTask的内部类
private volatile WaitNode waiters;
waiters等待队列:这个是单向队列里面是等待本任务执行结果的线程。比如ABCD四个线程其中A线程执行了任务B、C、D线程都等A线程的返回结果就需要在waiters等待队列里等着。
//等待队列源码static final class WaitNode {//被阻塞的线程也就volatile Thread thread;//这里看出它就是个单向队列volatile WaitNode next;WaitNode() { thread Thread.currentThread(); }}3.3 run()方法原理
run方法比较简单不放源码直接说就是执行定义的callable任务任务执行完成后通过CAS去更新outCome返回值。 protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {outcome v;UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final statefinishCompletion();}} 3.4 get()方法原理
这个方法和run()一样重要其他线程想要获取本任务结果都是通过get方法读取。逻辑也是很简单。其中waitDone()方法就是进入等待等列等结果。 public V get() throws InterruptedException, ExecutionException {int s state;//1、如果任务没执行完成当前想读取任务结果的线程就进入阻塞等待if (s COMPLETING)s awaitDone(false, 0L);//2、如果任务执行完成就返回结果 return report(s);}3.5 其他方法 //当前任务状态是否被取消直接读状态值public boolean isCancelled() {return state CANCELLED;}//当前任务状态是否以及执行结束直接读状态值public boolean isDone() {return state ! NEW;}
本系列文章推荐
1、JAVA并发编程系列(12)ThreadLocal就是这么简单|建议收藏
2、JAVA并发编程系列(11)线程池底层原理架构剖析