互联网面试-请简单介绍高并发场景中的Future设计模式?  第1张

根据办理

在日常生活中,我们经常会碰到一种情况,就是在我们提出需求之后,因为办事者还没有筹办好对应的实物交付给我们的时候,就会先给我们一张根据,比及两边约定的时间到了之后,我们带着那个根据就能够去获取到我们想要的工具。例如在一个蛋糕店里我们定了一个蛋糕,店长给了我们一个根据,然后在约定的时间我们去蛋糕店中取走我们定的蛋糕。如许我们不需要在店里期待,而是去完成我们的其他工做。

在法式开发中假设有一个使命施行的时间比力长,凡是需要我们期待那个使命完成之后才气停止后续的使命,那个时候办事的挪用者就会不断期待。而我们操纵上面的介绍的根据的体例就完全能够处理挪用者期待的问题。也就是Java中常说的Future形式。下面我们就来看看关于Java中的Future形式。

Future设想形式

Future设想形式是从JDK1.5起头呈现的,在我们实现多线程操做的时候有常用的两种体例,一种是实现Runnbale接口,一种是继承Thread类,而我们不是太常用的一种体例就是Call接口的体例,那种体例就是类似于Future形式。其实现类图如下。

互联网面试-请简单介绍高并发场景中的Future设计模式?  第2张

下面我们就来看看Future设想形式的详细实现体例。

接口逻辑定义

Future接口

按照之前的阐发我们的票据应当具有两个功用,一个功用是能够通过它获取到我们想要的成果,另一个功用就是通过它我们能够晓得我们想要的蛋糕能否造做完成。如下所示。

public interface Future<T> { // 用于返回计算之后的成果 T get() throws InterruptedException; // 用于判断使命能否一般施行 boolean done();}

FutureService接口

FutureService 的次要感化就是使命的提交,而提交使命的体例有两种,一种是不需要返回值的,一种是需要返回值的。代码如下。

public interface FutureService<IN,OUT> { // 提交不需要停止返回的使命的时候 Future的get办法返回为空 Future<?> submit(Runnable runnable); // 提交需要返回值的使命的时候 Future的get办法就是获取到返回值 Future<OUT> submit(Task<IN,OUT>task,IN input); // 静态办法获取到实现实例对象 static <T,R> FutureService<T,R> newServcie(){ return new FutureServiceImpl<>(); }}

Task接口

有了票据,有了办事人员,接下来就是需要有我们需要完成的使命是什么,而Task就是用来供给给挪用者实现计算逻辑的使命。能够承受一个参数而且返回最初的成果。有点类似于Callable接口

@FunctionalInterfacepublic interface Task<IN,OUT> { //给定一个参数颠末计算之后得到一个成果 OUT get(IN input);}Future法式实现

Future接口实现

会发现那个接口实现除了实现Future接口的两个办法之外还增加了一个finish的办法,用来完成使命通知操做。

public class FutureTask<T> implements Future<T> { // 返回成果 PRivate T result; //使命能否完成 private boolean isDone = false; // 对象锁 private final Object LOCK = new Object(); @Override public T get() throws InterruptedException { synchronized (LOCK){ //当使命还没有完成的时候,挪用get办法会被挂起进入阻塞期待 while (!isDone){ LOCK.wait(); } // 返回计算成果 return result; } } protected void finish(T result){ synchronized (LOCK){ //balking 设想形式 if (isDone){ return; } // 计算完成,为result指定成果,而且将isDone设置为true,同时唤起期待中的线程 this.result = result; this.isDone = true; LOCK.notifyAll(); } } @Override public boolean done() { return isDone; }}

在FutureTask中利用了线程间的通信wait和notifyAll,当使命没有完成之前通过get办法获取接口,挪用方会进入到阻塞形态,曲到使命完成之后收到线程唤醒,finish办法领受到使命完成的通知,然后唤醒因为挪用了get办法而进入阻塞的线程。

FutureService实现

public class FutureServiceImpl<IN,OUT> implements FutureService<IN,OUT> { private final static String FUTURE_THREAD_PREFIX = "FUTURE-"; private final AtomicInteger nextCounter = new AtomicInteger(0); private String getNextName(){ return FUTURE_THREAD_PREFIX+nextCounter.getAndIncrement(); } @Override public Future<?> submit(Runnable runnable) { final FutureTask<Void> future = new FutureTask<>(); new Thread(()->{ runnable.run(); // 使命施行完毕之后将null做为参数返回 future.finish(null); },getNextName()).start(); return future; } @Override public Future<OUT> submit(Task<IN, OUT> task, IN input) { final FutureTask<OUT> future = new FutureTask<>(); new Thread(()->{ OUT result = task.get(input); // 使命施行完毕之后,将实在的成果通过finish的体例传递给future future.finish(result); },getNextName()).start(); return future; }}Future设想形式测试

那里我们提交一个没有返回值的使命,代码如下,

public class FutureTest { public static void main(String[] args) throws InterruptedException { FutureService<Void,Void> service = FutureService.newServcie(); Future<?> future = service.submit(()->{ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("使命完成!"); }); future.get(); }}

提交一个有返回值的使命

public class FutureTest { public static void main(String[] args) throws InterruptedException { FutureService<String,Integer> service = FutureService.newServcie(); Future<Integer> future = service.submit(input->{ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return input.length(); },"Hello"); System.out.println(future.get()); }}晋级获取成果的办法

从上面两个测试的返回成果来看,若是我们挪用了future的get办法,那么我们的法式就会进入到阻塞的形态,那个操做与我们的预期不太相契合,那个也是整个的Future形式不断存在的问题。那么我们若何去改良那个获取get办法期待的问题呢?那就引入了一个CallBack的机造。代码如下

@Override public Future<OUT> submit(Task<IN, OUT> task, IN input, Callback<OUT> callback) { final FutureTask<OUT> future = new FutureTask<>(); new Thread(()->{ OUT result = task.get(input); // 使命施行完毕之后,将实在的成果通过finish的体例传递给future future.finish(result); if (null!=callback){ callback.call(result); } },getNextName()).start(); return future; }

测试效果,会发现我们课能够不利用get办法就能够完成施行成果的获取。

public class FutureTest { public static void main(String[] args) throws InterruptedException { FutureService<String,Integer> service = FutureService.newServcie(); service.submit(input->{ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return input.length(); },"Hello",System.out::println); }}总结

在之前的分享中良多的处所我们都提到了Future设想形式,通过那篇文章我们也进一步的领会了关于Future设想形式的思惟。其核心思惟就是模仿了一个根据场景,通过那种体例来实现对CPU的高效操纵。当然那里我们给出的只是一个根底演示版本,此中的存在的问题还有良多,有兴趣的读者能够本身思虑相关内容的优化。