精华内容
下载资源
问答
  • java 异步任务与结果

    千次阅读 2016-06-22 22:49:43
    Callable, Future, FutureTaskCallable RunnableRunnable 介绍Runnable只是一个接口,它可以被任何类继承,它的实例通过线程执行Callable Runnable区别代码public interface Runnable { public abstract void ...

    Callable, Future, FutureTask

    Callable 与Runnable

    Runnable 介绍

    Runnable只是一个接口,它可以被任何类继承,它的实例通过线程执行

    Callable 与Runnable区别
    代码
    public interface Runnable {
    
        public abstract void run();
    }
    作用:
    1. 当做线程使用
    2. 当做任务被线程执行。
    特点
    1. 当做任务没有时run()方法没有返回值;
    2. 不能抛出异常;
    Callable 介绍
    源码
    public interface Callable<V> {
    
        V call() throws Exception;
    }

    Callable是一个任务接口,它定义了一个带有返回值的并且抛出异常的任务。

    作用
    1. 当做任务
    特点
    1. 方法call() 具有返回值
    2. 可抛出异常,支持泛型

    Future

    future 介绍

    future 代表异步操作运算的结果,它也是一个接口;
    常用于获取运行结果,查询是否完成,对任务结果进行取消

    public interface Future<V> {
    
        /**
    
           尝试取消当前任务,如果任务已经被完成,这个操作就失败返回fail,    
           参数 mayInterruptIfRunning 为ture 时,代表任务在执行中,也可以取消任务; 为false时,只能取消未被执行的任务。
    
         */
        boolean cancel(boolean mayInterruptIfRunning);
    
        /**
         * Returns {@code true} if this task was cancelled before it completed
         * normally.
         *
         * @return {@code true} if this task was cancelled before it completed
         */
        boolean isCancelled();
    
        /**
         返回任务是否完成。
         *
         * @return {@code true} if this task completed
         */
        boolean isDone();
    
        /**
         * 获取返回结果,有必要一直等在结果返回;
         */
        V get() throws InterruptedException, ExecutionException;
    
        /**
         * 获取执行结果,有必要就等待timeout , 指定时间内结果没有执行完,返回Null
         *
         * @param timeout the maximum time to wait
         * @param unit the time unit of the timeout argument
         * @return the computed result
         * @throws CancellationException if the computation was cancelled
         * @throws ExecutionException if the computation threw an
         * exception
         * @throws InterruptedException if the current thread was interrupted
         * while waiting
         * @throws TimeoutException if the wait timed out
         */
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }
    
    功能
    1. 取消任务
    2. 获取任务执行结果
    3. 判断任务是否完成

    FutureTask

    简述

    FutureTask 是一个类,

    部分源码
    public class FutureTask<V> implements RunnableFuture<V> {
    .....
    
    }
    
    RunnableFuture
    /**
    
     */
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        /**
         * Sets this Future to the result of its computation
         * unless it has been cancelled.
         */
        void run();
    }

    RunnableFuture 继承了Runnable, 和Future,它既可以当做任务被执行,又可以判断结果是完成,但获取不到具体的值,因为它的父类是Callable 而不是Runnable。

    FutureTask 是RunnableFuture 的子类实现。

    示例
    
    public class AsyncTask {
    
        public static void main(String args[]) throws InterruptedException, ExecutionException {
    
            ExecutorService service = Executors.newCachedThreadPool();
            MyTask1 task1 = new MyTask1();
    
            Future<String> result1 = service.submit(task1);
            System.out.println("...等待完成任务1。。执行结果是:" + result1.get());
    
            MyTask2 task2 = new MyTask2();
            Future<?> result2 = service.submit(task2);
            System.out.println("...等待完成任务2.。执行结果是。" + result2.get());
    
            FutureTask<String> ft = new FutureTask<String>(task1);
    
            Future<String> reuslt2 = (Future<String>) service.submit(ft);
    
            System.out.println("...等待任务3.。执行结果是:" + result2.get());
    
            service.shutdown();
    
        }
    
    
    }
    
    class MyTask2 implements Runnable {
    
        @Override
        public void run() {
    
            System.out.println("..执行Runnable任务。。");
    
        }
    
    }
    
    
    class MyTask1 implements Callable<String> {
    
        @Override
        public String call() throws Exception {
            System.out.println("...线程执行callable任务。");
            return "success";
        }
    
    }
    执行结果
    ...线程执行callable任务。
    ...等待完成任务1。。执行结果是:success
    ..执行Runnable任务。。
    ...等待完成任务2.。执行结果是。null
    ...等待任务3.。执行结果是:null
    ...线程执行callable任务。
    
    展开全文
  • 3.spring线程池FutureTask配合使用获取任务执行状态   用ThreadPoolExecutor的时候,又想知道被执行的任务的执行情况,这时就可以用FutureTask。  ThreadPoolTask: package zmx.spring....

            API  文档中很清楚,SpringFrameWork 的 ThreadPoolTaskExecutor 是辅助 JDK 的 ThreadPoolExecutor  的工具类,它将属性通过 JavaBeans 的命名规则提供出来,方便进行配置。

    1.JDK之ThreadPoolExecutor的使用

     Spring中的ThreadPoolTaskExecutor是借助于JDK并发包中的java.util.concurrent.ThreadPoolExecutor来实现的.下面先学习下ThreadPoolExecutor中的相关信息.ThreadPoolExecutor构造函数如下: 

    Java代码  收藏代码
    1. public ThreadPoolExecutor(int corePoolSize,  
    2.                           int maximumPoolSize,  
    3.                           long keepAliveTime,  
    4.                           TimeUnit unit,  
    5.                           BlockingQueue<Runnable> workQueue,  
    6.                           ThreadFactory threadFactory,  
    7.                           RejectedExecutionHandler handler) {  

    下面分别说下各项代表的具体意义: 
    int corePoolSize:线程池维护线程的最小数量. 
    int maximumPoolSize:线程池维护线程的最大数量. 
    long keepAliveTime:空闲线程的存活时间. 
    TimeUnit unit: 时间单位,现有纳秒,微秒,毫秒,秒枚举值. 
    BlockingQueue<Runnable> workQueue:持有等待执行的任务队列. 
    RejectedExecutionHandler handler: 用来拒绝一个任务的执行,有两种情况会发生这种情况。 
    一是:在execute方法中若addIfUnderMaximumPoolSize(command)为false,即线程池已经饱和; 
    二是:在execute方法中, 发现runState!=RUNNING || poolSize == 0,即已经shutdown,就调用ensureQueuedTaskHandled(Runnable command),在该方法中有可能调用reject。 

    Reject策略预定义有四种: 
    (1)ThreadPoolExecutor.AbortPolicy策略,是默认的策略,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。 
    (2)ThreadPoolExecutor.CallerRunsPolicy策略 ,调用者的线程会执行该任务,如果执行器已关闭,则丢弃. 
    (3)ThreadPoolExecutor.DiscardPolicy策略,不能执行的任务将被丢弃. 
    (4)ThreadPoolExecutor.DiscardOldestPolicy策略,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程). 

    2. Spring中ThreadPoolTaskExecutor的使用

     最常用方式就是做为BEAN注入到容器中,如下代码: 

    Java代码  收藏代码
    1. <bean id="threadPoolTaskExecutor"  
    2.     class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
    3.     <property name="corePoolSize" value="10" />  
    4.     <property name="maxPoolSize" value="15" />  
    5.     <property name="queueCapacity" value="1000" />  
    6. </bean>  

    ThreadPoolExecutor执行器的处理流程: 
    (1)当线程池大小小于corePoolSize就新建线程,并处理请求. 
    (2)当线程池大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理. 
    (3)当workQueue放不下新入的任务时,新建线程加入线程池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理. 
    (4)另外,当线程池的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁. 
    了解清楚了ThreadPoolExecutor的执行流程,开头提到的org.springframework.core.task.TaskRejectedException异常也就好理解了,ThreadPoolTaskExecutor类中使用的 就是ThreadPoolExecutor.AbortPolicy()策略,直接抛出异常。

    3.spring线程池与FutureTask配合使用获取任务执行状态

     

    用ThreadPoolExecutor的时候,又想知道被执行的任务的执行情况,这时就可以用FutureTask。

     ThreadPoolTask:

    package zmx.spring.threadpool.test;
    
    
    import java.io.Serializable; 
    import java.util.concurrent.Callable; 
       
    public class ThreadPoolTask implements Callable<String>, Serializable { 
    
         private static final long serialVersionUID = 0; 
         
         // 保存任务所需要的数据 
         private Object threadPoolTaskData; 
         private static int consumeTaskSleepTime = 2000; 
         public ThreadPoolTask(Object tasks) { 
             this.threadPoolTaskData = tasks; 
         }
         
    	 @Override
         public String call() throws Exception { 
            // 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句 
            System.out.println("开始执行任务:" + threadPoolTaskData); 
            String result = ""; 
            //便于观察,等待一段时间 
            try {  
                //Thread.sleep((long)(Math.random()*consumeTaskSleepTime));
                for (long i = 0; i < 10000000; i++) { 
                } 
    
                result = "OK"; 
            } catch (Exception e) { 
                e.printStackTrace(); 
                result = "ERROR"; 
            }  
            return result; 
        }
    
     
    } 
    
    


    模拟客户端提交的线程:

    package zmx.spring.threadpool.test;
    
    
    import java.util.concurrent.ExecutionException; 
    
    
    import java.util.concurrent.FutureTask; 
    import java.util.concurrent.TimeUnit; 
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
    
    public class StartTaskThread implements Runnable { 
    
         private ThreadPoolTaskExecutor threadPoolTaskExecutor; 
         private int i; 
         public StartTaskThread(ThreadPoolTaskExecutor threadPoolTaskExecutor, int i) { 
             this.threadPoolTaskExecutor = threadPoolTaskExecutor; 
             this.i = i; 
         } 
    
         @Override 
         public void run() { 
             String task = "task@ " + i; 
             System.out.println("创建任务并提交到线程池中:" + task); 
             FutureTask<String> futureTask = new FutureTask<String>(new ThreadPoolTask(task)); 
             threadPoolTaskExecutor.execute(futureTask); 
             // 在这里可以做别的任何事情 
             String result = null; 
             try { 
                 // 取得结果,同时设置超时执行时间为1秒。同样可以用future.get(),不设置执行超时时间取得结果 
                 // result = futureTask.get(1000, TimeUnit.MILLISECONDS);
                 while(true){
                     if(futureTask.isDone()){
                    	 result = futureTask.get();
                    	 break;
                     } 
                 }
    
             } catch (InterruptedException e) { 
                 futureTask.cancel(true); 
             } catch (ExecutionException e) { 
                 futureTask.cancel(true); 
             } catch (Exception e) { 
                 futureTask.cancel(true); 
                 //超时后,进行相应处理 
             } finally { 
                 System.out.println("task@" + i + ":result=" + result); 
             } 
    
         } 
    
    } 
    
    

    spring配置文件:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:jee="http://www.springframework.org/schema/jee" 
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd   
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd   
           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd   
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
         
     
        
       <bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> 
             <!-- 核心线程数,默认为1 --> 
             <property name="corePoolSize" value="10" /> 
             <!-- 最大线程数,默认为Integer.MAX_VALUE --> 
             <property name="maxPoolSize" value="50" /> 
    
             <!-- 队列最大长度,一般需要设置值: 大于等于notifyScheduledMainExecutor.maxNum;默认为Integer.MAX_VALUE -->
             <property name="queueCapacity" value="50" /> 
     
             <!-- 线程池维护线程所允许的空闲时间,默认为60s --> 
             <property name="keepAliveSeconds" value="300" /> 
             <!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 --> 
             <property name="rejectedExecutionHandler"> 
                 <!-- AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常 --> 
                 <!-- CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度 --> 
                 <!-- DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行 --> 
                 <!-- DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行 --> 
                 <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /> 
             </property> 
         </bean> 
        
        
    </beans>
    

    测试类:

    package zmx.spring.threadpool.test;
    
    
    import org.junit.Test; 
    import org.junit.runner.RunWith; 
    import org.springframework.beans.factory.annotation.Autowired; 
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 
    import org.springframework.test.context.ContextConfiguration;  
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 
    @RunWith(SpringJUnit4ClassRunner.class) 
    // 指定的运行runner,并且把你所指定的Runner作为参数传递给它 
    @ContextConfiguration(locations = "classpath:zmx/spring/threadpool/test/applicationContext.xml") 
    public class TestThreadPool{
    	
         private static int produceTaskSleepTime = 10; 
         private static int produceTaskMaxNumber = 100; 
         @Autowired 
         private ThreadPoolTaskExecutor threadPoolTaskExecutor; 
         
         public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() { 
             return threadPoolTaskExecutor; 
         } 
         public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) { 
             this.threadPoolTaskExecutor = threadPoolTaskExecutor; 
         } 
    
         @Test 
         public void testThreadPoolExecutor() { 
             for (int i = 1; i <= produceTaskMaxNumber; i++) { 
                 try { 
                     Thread.sleep(produceTaskSleepTime); 
                 } catch (InterruptedException e1) { 
                     e1.printStackTrace(); 
                 } 
                 new Thread(new StartTaskThread(threadPoolTaskExecutor, i)).start(); 
                 
                 
             } 
             
             threadPoolTaskExecutor.shutdown();
         } 
    
     } 
    
    

    运行结果:

    创建任务并提交到线程池中:task@ 1
    开始执行任务:task@ 1
    创建任务并提交到线程池中:task@ 2
    task@1:result=OK
    开始执行任务:task@ 2
    task@2:result=OK
    创建任务并提交到线程池中:task@ 3
    开始执行任务:task@ 3
    task@3:result=OK
    创建任务并提交到线程池中:task@ 4
    开始执行任务:task@ 4
    task@4:result=OK
    创建任务并提交到线程池中:task@ 5
    开始执行任务:task@ 5
    task@5:result=OK
    创建任务并提交到线程池中:task@ 6
    开始执行任务:task@ 6
    task@6:result=OK
    创建任务并提交到线程池中:task@ 7
    开始执行任务:task@ 7


     

     

     

     

     

     

     

    展开全文
  • 26 - Future 获取异步任务结果

    千次阅读 2020-07-05 10:42:25
      在上一篇文章《25 - ThreadPoolExecutor 线程池》中,我们详细介绍了如何创建正确的线程池,也粗略了讲了一下如何启动线程池,execute() 和 submit() 都是用来执行线程池任务的,它们最主要的区别是,submit() ...

      在上一篇文章《25 - ThreadPoolExecutor 线程池》中,我们详细介绍了如何创建正确的线程池,也粗略了讲了一下如何启动线程池,execute() 和 submit() 都是用来执行线程池任务的,它们最主要的区别是,submit() 方法可以接收线程池执行的返回值,而 execute() 不能接收返回值。 submit() 方法可以配合 Futrue 来接收线程执行的返回值,下面我们就来详细看看到底该如何获取任务的执行结果。

      

    1. 如何获取任务的执行结果

    1.1 ThreadPoolExecutor.submit

      Java 通过 ThreadPoolExecutor 提供的 3 个 submit() 方法和 1 个 FutureTask 工具类来支持获得任务执行结果的需求。下面我们先来介绍这 3 个 submit() 方法,这 3 个方法的方法签名如下:

    // 提交Runnable任务
    Future<?> submit(Runnable task);
    
    // 提交Callable任务
    <T> Future<T> submit(Callable<T> task);
    
    // 提交Runnable任务及结果引用  
    <T> Future<T> submit(Runnable task, T result);
    

      你会发现它们的返回值都是 Future 接口,Future 接口有 5 个方法,我都列在下面了,它们分别是取消任务的方法 cancel()、判断任务是否已取消的方法 isCancelled()、判断任务是否已结束的方法 isDone() 以及 2个获得任务执行结果的 get() 和 get(timeout, unit),其中最后一个 get(timeout, unit) 支持超时机制。通过 Future 接口的这 5 个方法你会发现,我们提交的任务不但能够获取任务执行结果,还可以取消任务。不过需要注意的是:这两个 get() 方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get() 方法的线程会阻塞,直到任务执行完才会被唤醒。

    // 取消任务
    boolean cancel(boolean mayInterruptIfRunning);
    
    // 判断任务是否已取消  
    boolean isCancelled();
    
    // 判断任务是否已结束
    boolean isDone();
    
    // 获得任务执行结果
    get();
    
    // 获得任务执行结果,支持超时
    get(long timeout, TimeUnit unit);
    

    这 3 个 submit() 方法之间的区别在于方法参数不同,下面我们简要介绍一下:

    1. 提交 Runnable 任务 submit(Runnable task) :这个方法的参数是一个 Runnable 接口,Runnable 接口的 run() 方法是没有返回值的,所以 submit(Runnable task) 这个方法返回的 Future 仅可以用来断言任务已经结束了,类似于 Thread.join();
    2. 提交 Callable 任务 submit(Callable task):这个方法的参数是一个 Callable 接口,它只有一个 call() 方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get() 方法来获取任务的执行结果;
    3. 提交 Runnable 任务及结果引用 submit(Runnable task, T result):这个方法很有意思,假设这个方法返回的 Future 对象是 f,f.get() 的返回值就是传给 submit() 方法的参数 result。这个方法该怎么用呢?下面这段示例代码展示了它的经典用法。需要你注意的是 Runnable 接口的实现类 Task 声明了一个有参构造函数 Task(Result r) ,创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run() 方法中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。
    public class TestFuture {
    
        public static void main(String[] args) throws Exception {
            Result result = new Result();
            result.setId(10086L);
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
                    60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
            Future<Result> resultFuture = threadPoolExecutor.submit(new Task(result), result);
            Result result1 = resultFuture.get();
            System.out.println(result == result1);
            System.out.println(result1.getId());
            System.out.println(result1.getDesc());
        }
    
        static class Task implements Runnable {
            private Result result;
    
            public Task(Result result) {
                this.result = result;
            }
    
            @Override
            public void run() {
                System.out.println("Result id is:" + result.getId());
                result.setDesc("这个一个 Future 的测试...");
            }
        }
    
        static class Result {
            private Long id;
            private String desc;
    
            public Long getId() {
                return id;
            }
    
            public void setId(Long id) {
                this.id = id;
            }
    
            public String getDesc() {
                return desc;
            }
    
            public void setDesc(String desc) {
                this.desc = desc;
            }
        }
    }
    
    # 运行结果如下:
    Result id is:10086
    true
    10086
    这个一个 Future 的测试...
    

      

    1.2 FutureTask 工具类

      前面我们提到的 Future 是一个接口,而 FutureTask 是一个实实在在的工具类,这个工具类有两个构造函数,它们的参数和前面介绍的 submit() 方法类似。

    FutureTask(Callable<V> callable);
    
    FutureTask(Runnable runnable, V result);
    

      那如何使用 FutureTask 呢?其实很简单,FutureTask 实现了 Runnable 和 Future 接口,由于实现了 Runnable 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。下面的示例代码是将 FutureTask 对象提交给 ThreadPoolExecutor 去执行。

    
    // 创建FutureTask
    FutureTask<Integer> futureTask = new FutureTask<>(()-> 1+2);
    
    // 创建线程池
    ExecutorService es = Executors.newCachedThreadPool();
    
    // 提交FutureTask 
    es.submit(futureTask);
    
    // 获取计算结果
    Integer result = futureTask.get();
    

      FutureTask 对象直接被 Thread 执行的示例代码如下所示。相信你已经发现了,利用 FutureTask 对象可以很容易获取子线程的执行结果。

    // 创建FutureTask
    FutureTask<Integer> futureTask = new FutureTask<>(()-> 1+2);
    
    // 创建并启动线程
    Thread T1 = new Thread(futureTask);
    T1.start();
    
    // 获取计算结果
    Integer result = futureTask.get();
    

      

    2. 实现最优的“烧水泡茶”程序

      记得以前初中语文课文里有一篇著名数学家华罗庚先生的文章《统筹方法》,这篇文章里介绍了一个烧水泡茶的例子,文中提到最优的工序应该是下面这样:

    在这里插入图片描述
      下面我们用程序来模拟一下这个最优工序。我们专栏前面曾经提到,并发编程可以总结为三个核心问题:分工、同步和互斥。编写并发程序,首先要做的就是分工,所谓分工指的是如何高效地拆解任务并分配给线程。对于烧水泡茶这个程序,一种最优的分工方案可以是下图所示的这样:用两个线程 T1 和 T2 来完成烧水泡茶程序,T1 负责洗水壶、烧开水、泡茶这三道工序,T2 负责洗茶壶、洗茶杯、拿茶叶三道工序,其中 T1 在执行泡茶这道工序时需要等待 T2 完成拿茶叶的工序。对于 T1 的这个等待动作,你应该可以想出很多种办法,例如 Thread.join()、CountDownLatch,甚至阻塞队列都可以解决,不过今天我们用 Future 特性来实现。

    在这里插入图片描述
      下面的示例代码就是用这一章提到的 Future 特性来实现的。首先,我们创建了两个 FutureTask——ft1 和 ft2,ft1 完成洗水壶、烧开水、泡茶的任务,ft2 完成洗茶壶、洗茶杯、拿茶叶的任务;这里需要注意的是 ft1 这个任务在执行泡茶任务前,需要等待 ft2 把茶叶拿来,所以 ft1 内部需要引用 ft2,并在执行泡茶之前,调用 ft2 的 get() 方法实现等待。

    public class TestFutureTask {
    
        public static void main(String[] args) throws Exception {
            FutureTask<String> ft1 = new FutureTask<>(new Task1());
            FutureTask<String> ft2 = new FutureTask<>(new Task2(ft1));
            Thread t1 = new Thread(ft1);
            t1.start();
            Thread t2 = new Thread(ft2);
            t2.start();
            System.out.println(ft2.get());
        }
    
        static class Task2 implements Callable<String> {
            private FutureTask<String> futureTask;
    
            public Task2(FutureTask futureTask) {
                this.futureTask = futureTask;
            }
    
            @Override
            public String call() throws Exception {
                System.out.println("线程2:吸水壶");
                Thread.sleep(1000);
                System.out.println("线程2:烧开水");
                Thread.sleep(1000);
                String result = futureTask.get();
                System.out.println("线程2:拿到茶叶:" + result);
                System.out.println("线程2:泡茶");
                Thread.sleep(1000);
                return "上茶喽...";
            }
        }
    
        static class Task1 implements Callable<String> {
            @Override
            public String call() throws Exception {
                System.out.println("线程1:洗茶壶");
                Thread.sleep(1000);
                System.out.println("线程1:洗茶杯");
                Thread.sleep(1000);
                System.out.println("线程1:拿茶叶");
                Thread.sleep(1000);
                return "龙井";
            }
        }
    }
    
    # 运行结果如下:
    线程1:洗茶壶
    线程2:吸水壶
    线程1:洗茶杯
    线程2:烧开水
    线程1:拿茶叶
    线程2:拿到茶叶:龙井
    线程2:泡茶
    上茶喽...
    

      

    3. 总结

      利用 Java 并发包提供的 Future 可以很容易获得异步任务的执行结果,无论异步任务是通过线程池 ThreadPoolExecutor 执行的,还是通过手工创建子线程来执行的。Future 可以类比为现实世界里的提货单,比如去蛋糕店订生日蛋糕,蛋糕店都是先给你一张提货单,你拿到提货单之后,没有必要一直在店里等着,可以先去干点其他事,比如看场电影;等看完电影后,基本上蛋糕也做好了,然后你就可以凭提货单领蛋糕了。

      利用多线程可以快速将一些串行的任务并行化,从而提高性能;如果任务之间有依赖关系,比如当前任务依赖前一个任务的执行结果,这种问题基本上都可以用 Future 来解决。在分析这种问题的过程中,建议你用有向图描述一下任务之间的依赖关系,同时将线程的分工也做好,类似于烧水泡茶最优分工方案那幅图。对照图来写代码,好处是更形象,且不易出错。

    展开全文
  • 1、scheduleAtFixedRate 方法,顾名思义,它的方法名称的意思是:已固定的频率来执行某项计划(任务)。  2、scheduleWithFixedDealy,相对固定的延迟后,执行某项计划。  还是比较简单明了的描述比较好:第一个...
    1、scheduleAtFixedRate 方法,顾名思义,它的方法名称的意思是:已固定的频率来执行某项计划(任务)。 
    2、scheduleWithFixedDealy,相对固定的延迟后,执行某项计划。 
    还是比较简单明了的描述比较好:第一个方法是固定的频率来执行某项计划,它不受计划执行时间的影响。到时间,它就执行。 

    而第二个方法,相对固定,据鄙人理解,是相对任务的。即无论某个任务执行多长时间,等执行完了,我再延迟指定的时间。也就是第二个方法,它受计划执行时间的影响。 

    package com;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class TestscheduleAtFixedRate {
    	public static void main(String[] args) {
            // TODO 自动生成的方法存根
            ScheduledExecutorService executor=Executors.newScheduledThreadPool(2);
            long startTime=System.currentTimeMillis();
           // executor.scheduleAtFixedRate(command, initialDelay, period, unit)
            executor.scheduleWithFixedDelay(new MyRunableA_atfix(), 0, 2, TimeUnit.SECONDS);
            
            
            System.out.println("A"+(System.currentTimeMillis()-startTime));
        }
    }
    
    class MyRunableA_atfix implements Runnable{
    	 
        @Override
        public void run() {
            // TODO 自动生成的方法存根
             long startTime=System.currentTimeMillis();
             
             System.out.println(startTime);
            //System.out.println("begin A "+System.currentTimeMillis());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis()-startTime);
            
           // System.out.println("end A "+System.currentTimeMillis());
        }
    }

    执行结果:

    A1
    1506748405777
    2001
    1506748409778
    2001
    1506748413780
    2001

    展开全文
  • 执行顺序其实很简单,同级别的宏任务优先于微任务执行,高一级别的微任务优先于宏任务执行(不一定对,按照我的理解去试了几道试题都蛮简单的)--这个同级别不好说明白,举个例子吧 首先我们看第一级别的宏任务 首...
  • 1.Callable接口 ...Callable的接口和Runnable接口的区别是:Callable有一个call方法能够得到任务执行结果,而Runnable的run方法无法得到返回结果。Callable的接口的定义如下: public interface Callable {
  • 定时任务与消息队列的区别

    千次阅读 2020-08-10 08:43:35
    从MQ抓取订单抓取到⼀个订单到来事件的话触发处理,对于前端⽤户来说看到的结果是已经 下单成功了,下单是不受任何影响的 本质不同 定时任务作业是时间驱动,⽽MQ是事件驱动; 时间驱动是不可代替的,⽐如⾦融系统每...
  • 1、CompletableFuture 可以很方便的实现异步任务的封装 并实现结果的联合等一系列操作,轻松实现 任务的并行 package com.csdn.test; import java.util.Arrays; import java.util.List; import java.util....
  • setTimeout和Promise区别(宏任务和微任务

    千次阅读 多人点赞 2018-10-10 00:14:40
    javascript的宏任务和微任务任务有Event Table、Event Queue,微任务有Event Queue 1.宏任务:包括整体代码script,setTimeout,setInterval; 2.微任务:Promise,process.nextTick 注:Promise立即执行,...
  • 同步任务与异步任务理解整理

    千次阅读 2019-02-14 10:58:01
    异步任务:不会立即执行的任务(异步任务又分为宏任务与任务) 常见的异步任务:Ajax,Dom事件操作,setTimeOut,promise的then方法,Node读取文件 任务在执行过程中的流程图展示 一.宏任务与任务的理解 在一个任务...
  • Linux计划任务执行结果和手动执行不一致,发生原因有三种: 以下是计划任务和脚本情况介绍,写出具体代码是为了说明第三种情况 [root@salt-master 09]# crontab -l */1 * * * * /bin/sh /root/test03/09/01.sh >/...
  • JavaScript 中的正常任务与任务

    千次阅读 2017-10-05 17:58:01
    这时,需要区分两种任务:正常任务(task)任务(microtask)。它们的区别在于,“正常任务”在下一轮Event Loop执行,“微任务”在本轮Event Loop的所有任务结束后执行。console.log(1);setTimeout(function
  • 通过代码说明ansible2.x playbook apiansible 1.9.x的区别,并通过调用callback返回模块执行结果
  • C++11 Tasks 线程与任务区别

    千次阅读 2018-09-02 18:28:37
    无论子线程还是promise都计算了3+4的和,并且返回了执行结果. std::async的调用在fut和std::async两端生成了一个数据通道。 fut是个future,std::async是个promise。 future通过调用fut.get()获取值。当然值是...
  • 定时任务就是在指定时间执行程序,或周期性执行计划任务。Java中实现定时任务的方法有很多,本文从从JDK自带的一些方法来实现定时任务的需求。 Timer和TimerTask 本文先介绍Java最原始的解决方案:Timer和TimerTask ...
  • JavaScript的宏任务与任务

    千次阅读 2019-04-08 06:34:07
    在介绍前端宏任务与任务之前,先列出来一道题,一块看一下。console.log('1') setTimeout(() => { console.log('2') }) new Promise((resolve, rejects) => { console.log('3') resolve() }).then(() =&...
  • EXP1: 在主线程上添加宏任务与任务 EXP2: 在微任务中创建微任务 EXP3: 宏任务中创建微任务 EXP4:微任务队列中创建的宏任务 总结 这篇博文仅为个人理解,文章内提供一些更加权威的参考,如有片面及错误,...
  • Promise对象任务、微任务

    千次阅读 2020-10-16 11:16:06
    它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。 setTimeout(function() { console.log(1); }, 0); new Promise(function (resolve...
  • 我想问下,在celery中,怎么使用kafka作为存储结果,我在worker中使用存储的时候,存本地速度是正常的,当使用kafka的时候每次任务都需要连接一次kafka导致任务太慢,感谢大家
  • spring-quartz普通任务与可传参任务

    千次阅读 2017-02-09 10:26:32
    转自http://www.thinksaas.cn/topics/0/679/679979.html两者区别与作用:普通任务:总调度(SchedulerFactoryBean)–> 定时调度器(CronTriggerFactoryBean) –> 调度明细自定义执行方法bean...–>调度bean(我们定义...
  • 我们可以按照任务的种类,将任务分为回归任务和分类任务.那这两者的区别是什么呢?按照较官方些的说法,输入变量输出变量均为连续变量的预测问题是回归问题,输出变量为有限个离散变量的预测问题成为分类问题。 回归 ...
  • 任务、微任务

    千次阅读 多人点赞 2018-10-10 10:58:47
    任务与任务? B-树中含511个关键字,B-树为3阶,则包含叶子节点层该树最大深度为? 8 9 10 11
  • 近几天发现一直好好的数据备份计划任务一直返回0x4失败,直接执行bat又是正常的。 bat命令中使用的是xcopy,到处找方案没解决。 今天意外在使用另一个命令时,发现提示:网络连接数据超过最大值。 于是到源共享...
  • crond是Linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,windows下的计划任务类似,在CentOS Linux release 7.2.1511中默认是开机启动的,大家可以使用命令:systemctl status crond进行查看。...
  • FreeRTOS专题五:空闲任务与阻塞延时

    千次阅读 2019-09-05 11:38:01
    空闲任务与阻塞延时
  • asyncio 系列一、asyncio 的协程与任务

    千次阅读 2019-05-24 15:46:41
    asyncio 的协程与任务 官网:https://docs.python.org/zh-cn/3/library/asyncio-task.html#scheduling-from-other-threads 一、协程 用async定义的函数,可以叫协程函数、异步函数,本文统一叫协程函数。 调用...
  • 综观目前的 Web 应用,多数应用都具备任务调度的功能。本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给需要开发任务调度的...
  • 双作业操作,双任务,双侧任务,这三个范式的区别是什么?|小白心理-312/347考研答疑 1.双作业操作范式 让被试同时操作两个任务,观察被试的完成情况。实验结果符合“互补原则”,即对一个任务投入的资源多,对另...
  • Linux计划任务以及进程检测控制

    千次阅读 2021-03-26 20:20:25
    Linux计划任务以及进程检测控制一、Linux中的计划任务1、什么是计划任务2、Linux中的计划任务3、计划任务的编辑4、几个小案例5、计划任务6、计划任务权限1)黑名单2)白名单7、查看计划任务的保存文件8、at命令二...
  • Activiti6自学之路(七)——个人任务和组任务

    万次阅读 热门讨论 2019-06-16 03:35:26
    一、个人任务与任务区别 个人任务:流程中的某个任务由指定的user来执行 组任务:流程中的某个任务由指定的group来完成,其中group由多个user组成 实例分析 1、填写请假申请任务:个人任务,比如张三提交请假申请...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 419,944
精华内容 167,977
关键字:

任务与结果的区别