美文网首页
java多线程:future

java多线程:future

作者: 徐士林 | 来源:发表于2017-04-15 23:26 被阅读176次

    首先future是一个接口,该接口用来接收线程异步返回的结果。
    熟悉多线程的应该知道,ExecutorService有下面两个方法可以提交任务。

    <T> Future<T> submit(Callable<T> task);
    void execute(Runnable command);
    

    callable和runnable差不多。但是runnable不会返回结果,并且无法抛出返回结果的异常。所以callable功能更加强大,它被线程执行后会返回一个值。这个值可以被Future拿到,也就是说Future可以拿到线程异步执行的结果。这个重点是异步的,也就说我们在等待线程执行结果的时候还可以做其他事情。

    Futrue可以监视目标线程调用call的情况,当你调用Future的get()方法以获得结果时,当前线程就开始阻塞,直接call方法结束返回结果。

    Future表示异步计算的结果,这个类中有如下一些方法

    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit);
    

    从上面的方法中我们可以看到,Future的监听状态是可以取消的。并且阻塞等待异步结果时也可以设置超时时间。

    Future实现类:FutureTask
    FutureTask实现了两个接口,分别是Runnable和Future。所以这个类既可以作为任务提交给executor也可以接收线程异步执行的结果。
    这样的组合是有一定道理的,这里涉及到Future模式。我们简单介绍一下,如果我们需要一个很耗时的返回值需要计算,而且这个返回值不是立刻就需要的。我们就可以用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,

    public class FutureTest {    
        public static void main(String[] args) {   
      
            //ExecutorService.submit()  
            ExecutorService threadPool = Executors.newSingleThreadExecutor();    
            Future<Integer> future = threadPool.submit(new Callable<Integer>() {    
                public Integer call() throws Exception {    
                    return new Random().nextInt(100);    
                }    
            });   
      
            try {    
                Thread.sleep(5000);// 可能做一些事情    
      
                int result = future.get()); //Future.get()  
      
            } catch (InterruptedException e) {    
                e.printStackTrace();    
            } catch (ExecutionException e) {    
                e.printStackTrace();    
            }    
        }    
    }  
    

    如果要执行多个带返回值的任务,并取得多个返回值,可用CompletionService:

    
    public class CompletionServiceDemo {
    
        public static class Task implements Callable<Integer> {
            private int i;
    
            Task(int i) {
                this.i = i;
            }
    
            @Override
            public Integer call() throws Exception {
                Thread.sleep(new Random().nextInt(5000));
                System.out.println(Thread.currentThread().getName() + "   " + i);
                return i;
            }
        }
    
        public void run() {
            ExecutorService pool = Executors.newFixedThreadPool(10);
            CompletionService<Integer> completionServcie = new ExecutorCompletionService<Integer>(
                    pool);
            try {
                for (int i = 0; i < 10; i++) {
                    completionServcie.submit(new CompletionServiceDemo.Task(i));
                }
                for (int i = 0; i < 10; i++) {
                    // take 方法等待下一个结果并返回 Future 对象。
                    // poll 不等待,有结果就返回一个 Future 对象,否则返回 null。
                    System.out.println(completionServcie.take().get());
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } finally {
                pool.shutdown();
            }
        }
    
        public static void main(String[] args) {
            new CompletionServiceDemo().run();
        }
    }
    

    可以看到,CompletionService可以提交多个带返回值的任务,CompletionService相当于Executor加上BlockingQueue,使用场景为当子线程并发了一系列的任务以后,主线程需要实时地取回子线程任务的返回值并同时顺序地处理这些返回值,谁先返回就先处理谁。

    如果不使用CompletionService,我们同样可以一个Future的集合来保存每一个任务的返回的值。但是我们遍历的集合有一个顺序,我们不能保证任务是按照这个顺序执行下去的。有可能后面的所有任务都已经完成,只有第一个没有完成。这是调用get方法仍然会阻塞住。如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。
    而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。

    相关文章

      网友评论

          本文标题:java多线程:future

          本文链接:https://www.haomeiwen.com/subject/ytwgnttx.html