Java线程池之Callable接口

基本概念

  • 常用的创建多线程的方式有两种:
    • 实现Runnable接口 : 适合资源共享
    • 继承Thread类 : 不适合资源共享
  • 问题: 这两种创建方式在多线程任务执行结束后,无法获取执行结果. 尽管可以通过采用共享变量或者共享存储区以及线程通信的方式来实现获取任务结果的目的
  • 为了解决线程无法获取多线程执行结果的问题,可以采用实现Callable接口

Callable接口

  • CallableRunnable比较:
    • Callable:
      • Callable可以返回多线程执行结果
      • Callable不能直接创建线程
      • Callable运行过程中可以抛出异常
      public interface Callable<V> {
      	V call() throws Exception;
      }
      
    • Runnable:
      • Runnable不能返回任何结果
      • Runnable可以直接创建线程
      • Runnable运行过程中不能抛出异常
      public interface Runnable {
      	public abstract void run();
      }
      

Future

  • Future: 保存多线程执行返回的结果
    • Future不能直接获取多线程执行返回的结果
    • Future是在主线程中跟踪其余线程的进度和执行返回的结果
  • Future使用示例
public interface Future<V> {

	/**
	 * 尝试取消当前任务的执行
	 * 	- 如果任务已经完成或者已经被取消则返回false
	 * 	- 如果任务还未启动,那么该线程则永远不会运行
	 * 	- 参数mayInterruptIfRunning用来表示是否中断正在执行的任务
	 *  
	 * @param mayInterruptIfRunning 是否中端正在执行的任务
	 * @return boolean 是否取消执行任务 
	 */
	boolean cancel(boolean mayInterruptIfRunning);

	/**
	 * 如果任务在正常完成之前被取消则返回true
	 *  
	 * @return boolean 任务是否取消
	 */
	boolean isCancelled();

	/**
	 * 如果任务完成则返回true,不管在何种情况下
	 *  
	 * @return boolean 任务是否完成
	 */
	boolean isDone();

	/**
	 * 等待结果计算完成后获取结果
	 *  
	 * @return V 计算的结果
	 * @throws CancellationException 计算取消时抛出异常
	 * @throws ExecutionException 计算时抛出异常
	 * @throws InterruptedException 如果当前线程等待时被中断
	 */
	V get() throws InterruptedException, ExecutionException;

	/**
	 * 等待指定的时间之后开始检索结果
	 *  
	 * @param timeout 指定等待的最长时间
	 * @param unit 时间的单位
	 * @return V 计算的结果
	 * @throws CancellationException 计算取消时抛出异常
	 * @throws ExecutionException 计算时抛出异常
	 * @throws InterruptedException 如果当前线程等待时被中断
	 * @throws TimeoutException 如果等待的时间超时 
	 */
	V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

FutureTask

  • 因为一个线程的创建既需要创建线程的Runnable, 也需要保存线程执行结果的Future, 所以有了FutureTask
  • FutureTask实现了同时继承自Runnable接口和Future接口的RunnableFuture接口
  • FutureTask对象可以通过传入Callable对象的构造器创建对象 ,FutureTask中也有包含要创建的线程对象,这样就可以使用Callable间接创建一个对象
  • FutureTask使用示例
	/**
	 * 任务的运行状态,初始化为NEW.运行状态只会在set(), setException()和cancel()方法中会被设置为终止状态
	 * 在完成期间,运行状态的值可能是正在设置结果时的COMPLETING和为了满足cancel的INTERRUPTING
	 * 因为状态的值是final修饰的唯一的无法进一步修改,所以从这些中间态到最终状态使用的是有序的惰性的写入
	 * 
	 * 存在以下可能的状态转换:
	 * 	- NEW -> COMPLETING -> NORMAL
	 * 	- NEW -> COMPLETING -> EXCEPTIONAL
	 * 	- NEW -> CANCELLED
	 * 	- NEW -> INTERRUPTING -> INTERRUPTED
	 */
	private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

	// 要执行的任务
	private Callable<V> callable;
	// 用来存储get()方法的返回值或者抛出的异常
	private Object outcome;
	// 执行任务的线程
	private volatile Thread runner;
	// 等待线程的堆栈
	private volatile WaitNode waiters;

	/**
	 * FutureTask构造器
	 * 创建了一个FutureTask,将在运行时执行给定的Callable 
	 * 
	 */
	public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // 初始值设置为NEW
        this.state = NEW;       
    }

	/**
	 * 等待结果计算完成后获取结果
	 */
	public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
        	// 如果状态小于COMPLETING,会进入awaitDone()方法,这是一个死循环,直到state变为NORMAL   
            s = awaitDone(false, 0L);
        // 调用report方法,传入值为NORMAL的state
        return report(s);
    }

	/**
	 * 为完成的任务返回结果或者抛出异常
	 */
	private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
        	// 当状态为NORMAL时,才会将返回值返回
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

	/**
	 * 执行FutureTask任务
	 * 
	 */
	public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            // 任务的初始状态为NEW
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                	// 调用call()方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                	// 调用set()方法
                    set(result);
            }
        } finally {
            // 运行器在状态确定为阻止run()并发调用时保持非空
            runner = null;
            // 必须在运行器为空之后重新读取状态以防止泄漏中断
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

	/**
	 * 将当前的Future设置为给定的结果除非当前的Future已经设置了值或者已经被取消
	 * 计算成功后,会在run()方法的内部调用该方法
	 *  
	 * @param v 设置为给定值
	 */
	protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
  • state的数值,从0 - NEW变为1 - COMPLETING变为2 - NORMAL时线程就执行完成了
  • 在执行过程中,当state的值从0 - NEW变为1 - COMPLETING变为2 - NORMAL时返回值才会保存到outcome
  • get() 方法是阻塞的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮Chova

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值