分享

Future和Callable的使用总结

 创始元灵6666 2022-06-17 发布于河北

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

1.Future和Callable的介绍

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

(1) 接口Callable的call()方法可以有返回值,但Runnable接口的run()方法没有返回值。

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

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

2.方法get()结合ExecutorService中的submit(Callable<T>)的使用

方法submit(Callable<T>)可以执行参数为Callable的任务,方法get()用于获得返回值,示例如下:

  1. package mycallable;
  2. import java.util.concurrent.Callable;
  3. public class MyCallable implements Callable<String> {
  4. private int age;
  5. public MyCallable(int age) {
  6. super();
  7. this.age = age;
  8. }
  9. public String call() throws Exception {
  10. Thread.sleep(8000);
  11. return "返回值 年龄是:" + age;
  12. }
  13. }
  1. package test.run;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.Future;
  4. import java.util.concurrent.LinkedBlockingDeque;
  5. import java.util.concurrent.ThreadPoolExecutor;
  6. import java.util.concurrent.TimeUnit;
  7. import mycallable.MyCallable;
  8. public class Run {
  9. public static void main(String[] args) throws InterruptedException {
  10. try {
  11. MyCallable callable = new MyCallable(100);
  12. ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L,
  13. TimeUnit.SECONDS, new LinkedBlockingDeque());
  14. Future<String> future = executor.submit(callable);
  15. System.out.println("main A " + System.currentTimeMillis());
  16. System.out.println(future.get());
  17. System.out.println("main B " + System.currentTimeMillis());
  18. } catch (ExecutionException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

3.方法get()结合ExecutorService中的submit(Runnable)和isDone()的使用

方法submit()不仅可以传入Callable对象,也可以传入Runnable对象,说明submit方法支持有返回值和无返回值的功能。如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性。

  1. package test.run;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. import java.util.concurrent.Future;
  6. /**
  7. * 如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性
  8. * @author linhaiy
  9. * @date 2019.03.06
  10. */
  11. public class Run {
  12. public static void main(String[] args) {
  13. try {
  14. Runnable runnable = new Runnable() {
  15. @Override
  16. public void run() {
  17. System.out.println("打印的信息");
  18. }
  19. };
  20. ExecutorService executorRef = Executors.newCachedThreadPool();
  21. Future future = executorRef.submit(runnable);
  22. System.out.println(future.get() + " " + future.isDone());
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. } catch (ExecutionException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }

4.使用ExecutorService接口中的方法submit(Runnable,T result)

方法submit(Runnable,T result)的第2个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得。

  1. package entity;
  2. public class Userinfo {
  3. private String username;
  4. private String password;
  5. public Userinfo() {
  6. super();
  7. }
  8. public Userinfo(String username, String password) {
  9. super();
  10. this.username = username;
  11. this.password = password;
  12. }
  13. public String getUsername() {
  14. return username;
  15. }
  16. public void setUsername(String username) {
  17. this.username = username;
  18. }
  19. public String getPassword() {
  20. return password;
  21. }
  22. public void setPassword(String password) {
  23. this.password = password;
  24. }
  25. }
  1. package myrunnable;
  2. import entity.Userinfo;
  3. public class MyRunnable implements Runnable {
  4. private Userinfo userinfo;
  5. public MyRunnable(Userinfo userinfo) {
  6. super();
  7. this.userinfo = userinfo;
  8. }
  9. @Override
  10. public void run() {
  11. userinfo.setUsername("usernameValue");
  12. userinfo.setPassword("passwordValue");
  13. }
  14. }
  1. package test;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.Future;
  4. import java.util.concurrent.FutureTask;
  5. import java.util.concurrent.LinkedBlockingDeque;
  6. import java.util.concurrent.ThreadPoolExecutor;
  7. import java.util.concurrent.TimeUnit;
  8. import myrunnable.MyRunnable;
  9. import entity.Userinfo;
  10. public class Test {
  11. FutureTask abc; //接口Future的实现类
  12. public static void main(String[] args) {
  13. try {
  14. Userinfo userinfo = new Userinfo();
  15. MyRunnable myrunnable = new MyRunnable(userinfo);
  16. ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 10,
  17. TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
  18. Future<Userinfo> future = pool.submit(myrunnable, userinfo);
  19. System.out.println(future);
  20. System.out.println("begin time=" + System.currentTimeMillis());
  21. userinfo = future.get();
  22. System.out.println("get value " + userinfo.getUsername() + " "
  23. + userinfo.getPassword());
  24. System.out.println(" end time=" + System.currentTimeMillis());
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. } catch (ExecutionException e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }

5.方法cancel(boolean mayInterruptIfRunning) 和isCancelled()的使用

方法cancel(boolean mayInterruptIfRunning)的参数mayInterruptIfRunning的作用是:如果线程正在运行则是否中断正在运行的线程,在代码中需要使用if(Thread.currentThread().isInterrupted())进行配合。

方法cancel()的返回值代表发送取消任务的命令是否成功完成。

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

方法get(long timeout,TimeUnit unit)的作用是在指定的最大时间内等待获得返回值。

  1. package mycallable;
  2. import java.util.concurrent.Callable;
  3. public class MyCallable implements Callable<String> {
  4. public String call() throws Exception {
  5. Thread.sleep(10000); //目的是为了在指定时间内获取不到返回值
  6. System.out.println("sleep 10秒执行完了!");
  7. return "anyString";
  8. }
  9. }
  1. package test.run;
  2. import java.util.concurrent.ExecutionException;
  3. import java.util.concurrent.Future;
  4. import java.util.concurrent.LinkedBlockingDeque;
  5. import java.util.concurrent.ThreadPoolExecutor;
  6. import java.util.concurrent.TimeUnit;
  7. import java.util.concurrent.TimeoutException;
  8. import mycallable.MyCallable;
  9. public class Run {
  10. public static void main(String[] args) {
  11. try {
  12. MyCallable callable = new MyCallable();
  13. ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L,
  14. TimeUnit.SECONDS, new LinkedBlockingDeque());
  15. System.out.println("begin " + System.currentTimeMillis());
  16. Future<String> future = executor.submit(callable);
  17. System.out.println("返回值" + future.get(5, TimeUnit.SECONDS));
  18. System.out.println(" end " + System.currentTimeMillis());
  19. } catch (InterruptedException e) {
  20. System.out.println("进入catch InterruptedException");
  21. e.printStackTrace();
  22. } catch (ExecutionException e) {
  23. System.out.println("进入catch ExecutionException");
  24. e.printStackTrace();
  25. } catch (TimeoutException e) {
  26. System.out.println("进入catch TimeoutException");
  27. e.printStackTrace();
  28. }
  29. }
  30. }

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

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

  1. package com.executionhandler;
  2. import java.util.concurrent.FutureTask;
  3. import java.util.concurrent.RejectedExecutionHandler;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5. public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
  6. public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
  7. System.out.println(((FutureTask) r).toString() + " 被拒绝!");
  8. }
  9. }
  1. package com.executionhandler;
  2. public class MyRunnable implements Runnable {
  3. private String username;
  4. public MyRunnable(String username) {
  5. super();
  6. this.username = username;
  7. }
  8. public void run() {
  9. System.out.println(username + " 在运行!");
  10. }
  11. }
  1. package com.executionhandler;
  2. import java.util.concurrent.ExecutorService;
  3. import java.util.concurrent.Executors;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5. public class Run {
  6. public static void main(String[] args) {
  7. ExecutorService service = Executors.newCachedThreadPool();
  8. ThreadPoolExecutor executor = (ThreadPoolExecutor) service;
  9. executor.setRejectedExecutionHandler(new MyRejectedExecutionHandler());
  10. service.submit(new MyRunnable("A"));
  11. service.submit(new MyRunnable("B"));
  12. service.submit(new MyRunnable("C"));
  13. executor.shutdown();
  14. service.submit(new MyRunnable("D"));
  15. }
  16. }

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

(1) 方法execute()没有返回值,而submit()方法可以有返回值。

(2) 方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义ThreadFactory的方式进行捕获,而submit()方法在默认的情况下,可以catch Execution-Exeception捕获异常

9.验证Future的缺点

  1. package mycallable;
  2. import java.util.concurrent.Callable;
  3. public class MyCallable implements Callable<String> {
  4. private String username;
  5. private long sleepValue;
  6. public MyCallable(String username, long sleepValue) {
  7. super();
  8. this.username = username;
  9. this.sleepValue = sleepValue;
  10. }
  11. @Override
  12. public String call() throws Exception {
  13. System.out.println(username);
  14. Thread.sleep(sleepValue);
  15. return "return " + username;
  16. }
  17. }
  1. package test;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.Callable;
  5. import java.util.concurrent.ExecutionException;
  6. import java.util.concurrent.Future;
  7. import java.util.concurrent.LinkedBlockingDeque;
  8. import java.util.concurrent.ThreadPoolExecutor;
  9. import java.util.concurrent.TimeUnit;
  10. import mycallable.MyCallable;
  11. public class Test {
  12. public static void main(String[] args) {
  13. try {
  14. MyCallable callable1 = new MyCallable("username1", 5000);
  15. MyCallable callable2 = new MyCallable("username2", 4000);
  16. MyCallable callable3 = new MyCallable("username3", 3000);
  17. MyCallable callable4 = new MyCallable("username4", 2000);
  18. MyCallable callable5 = new MyCallable("username5", 1000);
  19. List<Callable> callableList = new ArrayList<Callable>();
  20. callableList.add(callable1);
  21. callableList.add(callable2);
  22. callableList.add(callable3);
  23. callableList.add(callable4);
  24. callableList.add(callable5);
  25. List<Future> futureList = new ArrayList<Future>();
  26. ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
  27. new LinkedBlockingDeque<Runnable>());
  28. for (int i = 0; i < 5; i++) {
  29. futureList.add(executor.submit(callableList.get(i)));
  30. }
  31. System.out.println("run first time= " + System.currentTimeMillis());
  32. for (int i = 0; i < 5; i++) {
  33. System.out.println(futureList.get(i).get() + " " + System.currentTimeMillis());
  34. }
  35. // 按顺序打印的效果
  36. // 说明一个Future对应指定的一个Callable
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. } catch (ExecutionException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

总结:Future和Callable这两个接口的优点就是从线程中返回数据以便进行后期的处理,但是FutureTask类也有其自身的缺点,就是阻塞性。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多