Java线程实现的第三种方式Callable方式与结合Future获取返回值
多线程的实现方式有实现Runnable接口和继承Thread类(实际上Thread类也实现了Runnable接口),但是Runnable接口的方式有两个弊端,第一个是不能获取返回结果,第二个是不能抛出exception。但是Callable接口很好的解决了上面的问题。下面介绍Callable接口的使用方法。
0.我们先看JDKAPI对callable接口的解释:
public interface Callable<V>
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于 Runnable
,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
Executors
类包含一些从其他普通形式转换成 Callable 类的实用方法。
方法解释:
call
V call() throws Exception
- 计算结果,如果无法计算结果,则抛出一个异常。
- 返回:
- 计算的结果
- 抛出:
Exception
- 如果无法计算结果
1.第一个实现Callable接口开启线程的用法:(不接受返回值,只是开启线程执行任务)
package threadTest; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; /** * 实现callable接口,实现Callable接口 * * */ public class MyCallable implements Callable<String> { /** * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常 */ @Override public String call() { System.out.println(Thread.currentThread().getName() + " 执行callable的call方法"); return "result"; } public static void main(String[] args) { // 1.创建callable对象 Callable<String> myCallable = new MyCallable(); // 2.由上面的callable对象创建一个FutureTask对象 FutureTask<String> oneTask = new FutureTask<String>(myCallable); // 3.由FutureTask创建一个Thread对象 Thread t = new Thread(oneTask); // 4.开启线程 t.start(); } }
结果:
Thread-0 执行callable的call方法
解释:
(1)Callable接口不用解释了,就是一个类似于Runnable接口的接口,只有一个call方法,此方法有返回值,可以抛出异常。
(2)Futuretask类查看:实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口和Future接口:所以Futuretask是Runnable的对象(子类的对象也是父类的对象)
public class FutureTask<V> implements RunnableFuture<V> {
查看此类的构造方法:初始化成员变量callable
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
查看此类的run方法:(调用Callable的call()方法)
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
JDKAPI对此类的解释:
(3)RunnableFuture接口:
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
(4)Runnable接口不用解释了,Future接口的解释如下:===也就是Future用于获取Callable执行的返回结果
package java.util.concurrent; public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
JDKAPI的解释:
public interface Future<V>
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
用法示例(注意,下列各类都是构造好的。)
interface ArchiveSearcher { String search(String target); } class App { ExecutorService executor = ... ArchiveSearcher searcher = ... void showSearch(final String target) throws InterruptedException { Future<String> future = executor.submit(new Callable<String>() { public String call() { return searcher.search(target); }}); displayOtherThings(); // do other things while searching try { displayText(future.get()); // use future } catch (ExecutionException ex) { cleanup(); return; } } }
FutureTask
类是 Future 的一个实现,Future 可实现 Runnable,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造:
FutureTask<String> future = new FutureTask<String>(new Callable<String>() { public String call() { return searcher.search(target); }}); executor.execute(future);
(5)Thread类可以接受一个Runnable接口,上面代码FutureTask实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口,所以可以接受FutureTask对象。
2.使用ExecutorService、Callable、Future实现有返回结果的线程
1.单线程的获取返回结果:
package threadTest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 实现callable接口,实现Callable接口 * * */ public class MyCallable implements Callable<String> { /** * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常 */ @Override public String call() { System.out.println(Thread.currentThread().getName() + " 执行callable的call方法"); return "result"; } public static void main(String[] args) { test1(); } /** * 单个线程 */ public static void test1() { // 1.创建固定大小的线程池 ExecutorService es = Executors.newFixedThreadPool(1); // 2.提交线程任务,用Future接口接受返回的实现类 Future<String> future = es.submit(new MyCallable()); // 3.关闭线程池 es.shutdown(); // 4.调用future.get()获取callable执行完成的返回结果 String result; try { result = future.get(); System.out.println(Thread.currentThread().getName() + "\t" + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
结果:
pool-1-thread-1 执行callable的call方法
main result
2.多个线程的执行返回结果:
package threadTest; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 实现callable接口,实现Callable接口 * * */ public class MyCallable implements Callable<String> { /** * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常 */ @Override public String call() { System.out.println(Thread.currentThread().getName() + " 执行callable的call方法"); return "result"; } public static void main(String[] args) { test2(); } /** * 多个线程 */ public static void test2() { // 1.创建固定大小的线程池(5个) int threadNum = 5; ExecutorService es = Executors.newFixedThreadPool(threadNum); // 2.提交线程任务,用Future接口接受返回的实现类 List<Future<String>> futures = new ArrayList<Future<String>>(threadNum); for (int i = 0; i < threadNum; i++) { Future<String> future = es.submit(new MyCallable()); futures.add(future); } // 3.关闭线程池 es.shutdown(); // 4.调用future.get()获取callable执行完成的返回结果 for (Future<String> future : futures) { try { String result = future.get(); System.out.println(Thread.currentThread().getName() + "\t" + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } }
结果:
pool-1-thread-1 执行callable的call方法
pool-1-thread-2 执行callable的call方法
pool-1-thread-4 执行callable的call方法
pool-1-thread-3 执行callable的call方法
main result
pool-1-thread-5 执行callable的call方法
main result
main result
main result
main result
总结:
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
线程池的使用参考:https://www.cnblogs.com/qlqwjy/p/9470414.html