Future和Callable的使用

在默认情况下,线程 Thread 对象不具有返回值的功能,如果在需要取得返回值的情况下是极为不方便的,但在 Javal.5 的并发包中可以使用Future和Callable 来使线程具有返回值的功能。

Future和Callable 的介绍

接口 Callable 与线程功能密不可分,但和 Runnable 主要区别为:

(1) Callable 接口的 call(方法可以有返回值,而 Runnable 接口的 run()方法没有返回值。
(2)Callable 接口的 call()方法可以声明抛出异常,而 Runnable 接口的 run()方法不可以声明抛出异常

执行完 Callable 接口中的任务后,返回值是通过 Future 接口进行获得的。

方法 get()结合submit(Callable)的使用

get()方法可以获取Callable接口的返回值

类AbstractExecutorService中submit(Callable)源码

 public <T> Future<T> submit(Callable<T> task) {
        if (task == null) {
            throw new NullPointerException();
        } else {
            RunnableFuture<T> ftask = this.newTaskFor(task);
            this.execute(ftask);
            return ftask;
        }
    }

示例

public class FutureDemo {

    public static class MyCallable implements Callable<String>{
        @Override
        public String call() throws Exception {
            //模拟任务执行时间
            Thread.sleep(1000);
            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Future是submit()方法的返回值
        Future future = executorService.submit(new MyCallable());
        //从Future中取得返回值
        System.out.println("任务的返回值:"+future.get());
    }
}

任务的返回值:pool-1-thread-1执行完毕

由结果可知,submit()方法的返回值是Future对象(接口),通过Future的get()方法成功获取了Callable对象的返回值。

方法 get()结合submit(Runnable)的使用

get()方法无法获取Runnable接口的返回值,因为Runnable没有返回值。

类AbstractExecutorService中submit(Runnable)源码

 public Future<?> submit(Runnable task) {
        if (task == null) {
            throw new NullPointerException();
        } else {
            RunnableFuture<Void> ftask = this.newTaskFor(task, (Object)null);
            this.execute(ftask);
            return ftask;
        }
    }

示例

public class FutureDemo {
   public static class MyRunnable implements Runnable{

       @Override
       public void run() {
           try {
               //模拟任务执行时间
               Thread.sleep(1000);
               System.out.println(Thread.currentThread().getName()+"执行完毕");
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Future是submit()方法的返回值
        Future future = executorService.submit(new MyRunnable());
        //从Future中取得返回值
        System.out.println("任务的返回值:"+future.get());
    }
}

pool-1-thread-1执行完毕
任务的返回值:null

实验可知,Future对象没有获取到任何值,因为Runnable对象无法给定返回值。

方法 get()结合submit(Runnable, T result)的使用

Runnable对象没有返回值,submit(Runnable, T result)的result对象可以作为返回值,通过get()获取。

类AbstractExecutorService中submit(Runnable, T result)源码

public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) {
            throw new NullPointerException();
        } else {
            RunnableFuture<T> ftask = this.newTaskFor(task, result);
            //正常执行传入的task任务
            this.execute(ftask);
            return ftask;
        }
    }
    
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
       //value是一开始submit(Runnable task, T result)方法的result对象
       //创建了一个FutureTask对象,把submit()方法参数T result给了FutureTask对象
        return new FutureTask(runnable, value);
    }

示例

public class FutureDemo {
    //user对象,引用类型
    public static class User {
        private String name;
        private String age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }

        public User(String name, String age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    '}';
        }
    }

    public static class MyRunnable implements Runnable{
       String result = "结果2";
       public MyRunnable(User u) {
           super();
            u.setName("更改后姓名");
            u.setAge("更改后年龄");
       }

       @Override
       public void run() {
           try {
               //模拟任务执行时间
               Thread.sleep(1000);
               System.out.println(Thread.currentThread().getName()+"执行完毕");
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        User user = new User("原姓名","原年龄");
        //Future是submit()方法的返回值
        Future<User> future = executorService.submit(new MyRunnable(user),user);
        //从Future中取得返回值
        System.out.println("任务的返回值:"+future.get().toString());
    }
}

pool-1-thread-1执行完毕
任务的返回值:User{name='更改后姓名', age='更改后年龄'}

submit(Runnable, T result)对象将预先传给submit()方法的参数作为返回值,返回了。本实验中,我们用了User这个对象,实现进行了赋值,再传给Runnable对象,当任务执行时,对User对象的值进行了修改,此时作为submit()参数的User对象值就改变了,从而实现了Runnable可以返回值的效果。

Future接口方法boolean isDone()使用

boolean isDone()是判断任务是否执行完。
示例

public class FutureDemo {
    //Callable对象
    public static class MyCallable implements Callable<String>{
        @Override
        public String call() throws Exception {
            //模拟任务执行时间
            Thread.sleep(1000);
            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Future是submit()方法的返回值
        Future future = executorService.submit(new MyCallable());
        //是否执行完毕 MyCallable任务模拟执行任务时间1s
        System.out.println("任务是否执行完毕:" + future.isDone());
        //从Future中取得返回值
        System.out.println("任务的返回值:"+future.get());
        //是否执行完毕
        System.out.println("任务是否执行完毕:" + future.isDone());
    }
}

任务是否执行完毕:false
任务的返回值:pool-1-thread-1执行完毕
任务是否执行完毕:true

方法 cancel(boolean mayInterruptlfRunning和 isCancelled()的使用

1.方法cancel(boolean maylnterruptltRunning的参数mayInterruptlfRunning的作用是:如果线程正在运行则是否中断正在运行的线程,在代码中需要使用 if(Thread.currentThread(). islnterrupted ())进行配合。
2.方法cancel()的返回值代表发送取消任务的命令是否成功完成。
3.cancel(boolean )方法取消不了已经执行完的任务,cancel(true)可以取消正在执行的任务,cancel(false)取消不了正在执行的任务。
4.cancel(boolean )方法的取消只是将线程的isInterrupted进行了更改,并不是正在意义的中断。
示例

public class FutureDemo {
    //Callable对象
    public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() {
            try {
                //模拟任务执行时间
                Thread.sleep(1000);
                System.out.println(name+"执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,3,10,TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        //Future是submit()方法的返回值
        Future future1 = threadPoolExecutor.submit(new MyCallable("future1"));
        Thread.sleep(1100);
        //此时future1对应任务已经执行完,无法取消
        System.out.println("future1取消方法是否执行成功:" + future1.cancel(true)+";是否已经取消:" + future1.isCancelled());
        Future future2 = threadPoolExecutor.submit(new MyCallable("future2"));
        Thread.sleep(500);
        //此时future2对应任务正在执行,cancel(true)表示可以取消正在执行任务
        System.out.println("future2取消方法是否执行成功:" + future2.cancel(true)+";是否已经取消:" + future2.isCancelled());
        Future future3 = threadPoolExecutor.submit(new MyCallable("future3"));
        Thread.sleep(1500);
        //此时future3对应任务正在执行,cancel(false)表示无法取消正在执行任务
        System.out.println("future3取消方法是否执行成功:" + future3.cancel(false)+";是否已经取消:" + future3.isCancelled());
    }
}


future1执行完毕
future1取消方法是否执行成功:false;是否已经取消:false
future2取消方法是否执行成功:true;是否已经取消:true
//MyCallable代码有sleep,所以在正确取消任务,线程isInterrupted为true之后,会抛异常
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.example.demo.FutureDemo$MyCallable.call(FutureDemo.java:18)
	at com.example.demo.FutureDemo$MyCallable.call(FutureDemo.java:7)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)
future3执行完毕
future3取消方法是否执行成功:false;是否已经取消:false

取消方法只是将isInterrupted进行了更改,在实际使用中要在MyCallable中进行,中断状态的判断。
示例

 public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() {
            try {
                while(true){
                    //结合此代码 才能正确取消线程
                    if(Thread.currentThread().isInterrupted() == true){
                        throw new InterruptedException();
                    }
                    System.out.println("name+正在执行。。。");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return Thread.currentThread().getName()+"执行完毕";
        }
    }

方法 get(long timeout, TimeUnit unit)的使用

方法get(long timeout, TimeUnit unit)的作用是在指定最大时间内取得返回值。防止一直在堵塞,在等待线程结束返回值。

示例

public class FutureDemo {
    //Callable对象
    public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() {
            try {
                //模拟任务执行时间
                Thread.sleep(10000);
                System.out.println(name+"执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            threadPoolExecutor = new ThreadPoolExecutor(1,3,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
            //Future是submit()方法的返回值
            Future future1 = threadPoolExecutor.submit(new MyCallable("future1"));
            System.out.println(future1.get(5L,TimeUnit.SECONDS));
        } catch (Exception e) {
         e.printStackTrace();
        }
    }
}

java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:204)
	at com.example.demo.FutureDemo.main(FutureDemo.java:35)
future1执行完毕

本次实验,线程任务执行时间是10s中,而get方法只等待5s中,到时间后,没有等待返回值抛异常:TimeoutException。

异常的处理

接口Callable 任务在执行时有可能会出现异常,那在Callable 中异常是如何处理的呢?

Callable 任务抛异常,主线程没有调用get()方法

此时异常不会被主线程捕获

public class FutureDemo {
    //Callable对象
    public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() throws Exception{
            //模拟任务执行时间
            Thread.sleep(1000);
            //模拟异常,call()方法直接将异常抛出
            Integer.parseInt("a");
            System.out.println(name+"执行完毕");
            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            threadPoolExecutor = new ThreadPoolExecutor(1,3,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
            //Future是submit()方法的返回值
            Future future1 = threadPoolExecutor.submit(new MyCallable("future1"));
            //System.out.println(future1.get(5L,TimeUnit.SECONDS));
        } catch (Exception e) {
         e.printStackTrace();
        }
    }
}

控制台为空!

Callable 任务抛异常,主线程调用get()方法

此时异常被主线程捕获

public class FutureDemo {
    //Callable对象
    public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() throws Exception{
            //模拟任务执行时间
            Thread.sleep(1000);
            //模拟异常,call()方法直接将异常抛出
            Integer.parseInt("a");
            System.out.println(name+"执行完毕");
            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            threadPoolExecutor = new ThreadPoolExecutor(1,3,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
            //Future是submit()方法的返回值
            Future future1 = threadPoolExecutor.submit(new MyCallable("future1"));
            System.out.println(future1.get(5L,TimeUnit.SECONDS));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "a"
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:205)
	at com.example.demo.FutureDemo.main(FutureDemo.java:32)
Caused by: java.lang.NumberFormatException: For input string: "a"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.base/java.lang.Integer.parseInt(Integer.java:652)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at com.example.demo.FutureDemo$MyCallable.call(FutureDemo.java:19)
	at com.example.demo.FutureDemo$MyCallable.call(FutureDemo.java:7)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

自定义拒绝策略 RejectedExecutionHandler 接口的使用接口

RejectedExecutionHandler接口的主要作用是当线程池关闭后依然有任务要执行时,可以实现一些处理。

public class FutureDemo {

    public static class MyRejectedExecutionHandler implements RejectedExecutionHandler{
        @Override
        public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) {
            System.out.println(((FutureTask)runnable).toString() + "被拒绝");
        }
    }

    //Callable对象
    public static class MyCallable implements Callable<String>{
        String name;
        public MyCallable(String name) {
            super();
            this.name = name;
        }

        @Override
        public String call() throws Exception{
            //模拟任务执行时间
            Thread.sleep(1000);
            System.out.println(name+"执行完毕");
            return Thread.currentThread().getName()+"执行完毕";
        }
    }

    //main方法
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = null;
        try {
            threadPoolExecutor = new ThreadPoolExecutor(1,3,10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
            //使用自定义的拒绝策略
            threadPoolExecutor.setRejectedExecutionHandler(new MyRejectedExecutionHandler());
            //Future是submit()方法的返回值
            Future future1 = threadPoolExecutor.submit(new MyCallable("future1"));
            Future future2 = threadPoolExecutor.submit(new MyCallable("future2"));
            Future future3 = threadPoolExecutor.submit(new MyCallable("future3"));
            threadPoolExecutor.shutdown();
            Future future4 = threadPoolExecutor.submit(new MyCallable("future4"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} 	 

java.util.concurrent.FutureTask@5010be6[Not completed, task = com.example.demo.FutureDemo$MyCallable@2e0fa5d3]被拒绝
future1执行完毕
future2执行完毕
future3执行完毕	 	 	

方法 execute()与 submit()的区别

1.方法 execute()没有返回值,而 submit()方法可以有返回值.
2.方法 execute()在默认的情况下异常直接抛出,主不能捕获,但也可以通过自定义 ThreadFactory 的方式进行捕获,而 submit()方法在默认的情况下,可以 catchExecutionException 捕获异常,在get()方法获取返回值时可以在主线程捕获异常。

验证 Future 的缺点

接口 future 实现类是futureTask.java ,而且在使用线程池时,默认的情况下也是使用 futureTask. java 类作为接口 Future 实现类,也就是说,如果在使用 future和Callable情况下,使用 Future 接口也就是在使用 futureTask.java类。
Callable 接口与 Runnable 接口在对比时主要的优点是,Callable 接口可以通过 Future取得返回值,但是需要注意的是,Future 接口调用 get()方法取得处理的结果值时是阻塞性的,也就是如果调用Future 对象的 get()方法时,任务尚未执行完成,则调用 get()方法时一直堵塞到此任务完成时为止,如果是这样的效果,则前面先执行的任务一旦耗时很多,则后面的任务调用 get()方法就呈阻塞状态,也就是排队进行等待,大大影响运行效率,也就是主线程并不能保证首先获得的是最先完成任务的返回值,这就future 的缺点,影响效率。
根据这个特性, JDKl.5 提供了CompletionService 接口可以解决这个问题。

总结

对 Future Callable 接口中的全部 API 进行了介绍,这两个接口的优点就是从线程中返回数据以便进行后期的处理,但 FutureTask 类也有其自身的缺点,就是阻塞’性,解决这个缺点请参看Completion Service 的使用----->CompletionService 的使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值