Callable接口详解
- Callable: 返回结果并且可能抛出异常的任务。
- 优点:
- 可以获得任务执行返回值;
- 通过与Future的结合,可以实现利用Future来跟踪异步计算的结果。
Runnable和Callable的区别:
- 1、Callable规定的方法是call(),Runnable规定的方法是run().
- 2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
- 3、call方法可以抛出异常,run方法不可以
- 4、运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
- 5、代码示例:
-
//Callable 接口 public interface Callable<V> { V call() throws Exception; } // Runnable 接口 public interface Runnable { public abstract void run(); }
Future接口
- Future是一个接口,代表了一个异步计算的结果。接口中的方法用来检查计算是否完成、等待完成和得到计算的结果。
- 当计算完成后,只能通过get()方法得到结果,get方法会阻塞直到结果准备好了。
- 如果想取消,那么调用cancel()方法。其他方法用于确定任务是正常完成还是取消了。
- 一旦计算完成了,那么这个计算就不能被取消。
FutureTask类
- FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以说FutureTask是一个提供异步计算的结果的任务。
- FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor(如上面例子那样)。
Callable两种执行方式
- 1、借助FutureTask执行
- FutureTask类同时实现了两个接口,Future和Runnable接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
- 具体流程:
-
//定义实现Callable接口的的实现类重写call方法。 public class MyCallableTask implements Callable<Integer>{ @Override public Integer call() throws Exception { //TODO 线程执行方法 } } --------------------------------------------------------- //创建Callable对象 Callable<Integer> mycallabletask = new MyCallableTask(); //开始线程 FutureTask<Integer> futuretask= new FutureTask<Integer>(mycallabletask); new Thread(futuretask).start(); -------------------------------------------------------- 通过futuretask可以得到MyCallableTask的call()的运行结果: futuretask.get();
-
- 2、借助线程池来运行
- 线程池中执行Callable任务原型:
-
public interface ExecutorService extends Executor { //提交一个Callable任务,返回值为一个Future类型 <T> Future<T> submit(Callable<T> task); //other methods... }
-
- 借助线程池来运行Callable任务的一般流程为:
-
ExecutorService exec = Executors.newCachedThreadPool(); Future<Integer> future = exec.submit(new MyCallableTask());
- 通过future可以得到MyCallableTask的call()的运行结果: future.get();
-
- 线程池中执行Callable任务原型:
举例说明
- 例1:
-
public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException,TimeoutException{ //创建一个线程池 ExecutorService executor = Executors.newCachedThreadPool(); Future<String> future = executor.submit(()-> { TimeUnit.SECONDS.sleep(5); return "CallableTest"; }); System.out.println(future.get()); executor.shutdown(); } }
-
- 例2:Callable任务借助FutureTask运行:
-
public class CallableAndFutureTask { Random random = new Random(); public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { public Integer call() throws Exception { return random.nextInt(10000); } }; FutureTask<Integer> future = new FutureTask<Integer>(callable); Thread thread = new Thread(future); thread.start(); try { Thread.sleep(2000); System.out.println(future.get()); } catch (Exception e) { e.printStackTrace(); } } }
-
- 例3:Callable任务和线程池一起使用,然后返回值是Future:
-
public class CallableAndFuture { Random random = new Random(); public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<Integer> future = threadPool.submit(new Callable<Integer>() { public Integer call() throws Exception { return random.nextInt(10000); } }); try { Thread.sleep(3000); System.out.println(future.get()); } catch (Exception e) { e.printStackTrace(); } } }
-
- 例4:当执行多个Callable任务,有多个返回值时,我们可以创建一个Future的集合:
-
class MyCallableTask implements Callable<String> { private int id; public OneTask(int id){ this.id = id; } @Override public String call() throws Exception { for(int i = 0;i<5;i++){ System.out.println("Thread"+ id); Thread.sleep(1000); } return "Result of callable: "+id; } } public class Test { public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); ArrayList<Future<String>> results = new ArrayList<Future<String>>(); for (int i = 0; i < 5; i++) { results.add(exec.submit(new MyCallableTask(i))); } for (Future<String> fs : results) { if (fs.isDone()) { try { System.out.println(fs.get()); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println("MyCallableTask任务未完成!"); } } exec.shutdown(); } }
-
StopWatch的使用
- Spring提供的计时器StopWatch对于秒、毫秒为单位方便计时的程序,尤其是单线程、顺序执行程序的时间特性的统计输出支持比较好。也就是说假如我们手里面有几个在顺序上前后执行的几个任务,而且我们比较关心几个任务分别执行的时间占用状况,希望能够形成一个不太复杂的日志输出,StopWatch提供了这样的功能。而且Spring的StopWatch基本上也就是仅仅为了这样的功能而实现。
-
public String call() throws Exception { StopWatch stopWatch = new StopWatch(); stopWatch.start("测试StopWatch"); //TODO 业务逻辑 stopWatch.stop(); return "test"; }