java多线程之Callable+Future+FutureTask原理详解和简单使用

一、Runnable和Callable

Runnable是一个接口,只声明了一个run()方法,此方法为void类型,所以只能执行无返回值的任务。

public interface Runnable {
    public abstract void run();
}

Callable是java.util.concurrent包下的一个接口,提供了一个call()方法:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

该方法返回的数据类型就是传进来的泛型类型,主要用于计算产生结果。如果没有计算出结果,则会抛出异常。

二、Future

Future则是对于具体的RunnableCallable任务的执行结果进行取消、判断是否完成、获取任务执行结果。
Future接口声明了5个方法:

 boolean cancel(boolean mayInterruptIfRunning);

尝试取消此任务的执行。如果取消成功则返回true,取消失败则返回false。
如果任务已完成、已取消或由于其他原因无法取消,则此尝试将失败,返回false。
传入的参数如果为true,则直接中断正在执行的任务,返回true;如果为false,则允许正在进行的任务完成。

boolean isCancelled();

任务是否被取消成功,如果此任务在正常工作之前被取消,则返回true。

boolean isDone();

如果此任务已完成,则返回true。完成可能是由于正常终止、异常或取消 - 在所有这些情况下,此方法将返回 true。

V get() throws InterruptedException, ExecutionException;

以阻塞的方式获取任务执行结果,知道任务执行完毕返回。
另外在以下情况会抛出异常:
1.任务被取消:
抛出 CancellationException 异常
2.计算引发异常:
抛出 ExecutionException 异常
3.当前线程被中断:
抛出 InterruptedException 异常

V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;

同上,但规定了超时时间,如果在规定时间内没有计算出结果,会抛出TimeoutException异常。

三、FutureTask

FutureTask类实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future接口,可知FutureTask可做Runnable和Future一样的事情。

public class FutureTask<V> implements RunnableFuture<V> 

public interface RunnableFuture<V> extends Runnable, Future<V> 

3.1 首先看FutureTask的成员变量

 	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;
    private Object outcome; // non-volatile, protected by state reads/writes
    private volatile Thread runner;
    private volatile WaitNode waiters;

1.state:state保存了FutureTask任务的状态值,为volatile类型,保证了原子性,即任何一个线程修改了state的值,都可以保证其他线程看到修改后的值。
state共有如下几个取值(对应的值依此为0到6):

NEW:为state的初始值。表示该任务是个新任务。
COMPLETING:当前任务已经执行完毕或执行任务过程中出现了异常,但结果尚未保存到outcome(outcome用来保存计算结果或者是异常信息)中,属于中间状态
NORMAL: 任务执行完毕,且结果保存到outcome中。此为最终状态
EXCEPTIONAL: 任务执行过程中出现异常,且异常信息已保存到outcome中。此为最终状态
CANCELLED:任务尚未开始或任务执行时调用了cancel(false)方法。此为最终状态
INTERRUPTING:任务尚未开始或任务执行时调用了cancel(true)方法。此为中间状态
INTERRUPTED: 在调用cancel(true)时,会调用:

	Thread t = runner;
       if (t != null)
       t.interrupt();

state会变为INTERRUPTED,此为最终状态。但如果被终端的线程正在sleep()、wait()、或join(),则会抛出InterruptedException()异常,因此cancel(true)
(注意:中间状态时间短暂,不代表任务仍在执行,而是任务执行完毕后尚未设置执行结果,因此除了NEW状态,其余状态都代表已经执行结束)
可能出现的转变状态为:

NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED

2.callable:要执行的task任务。
3.outcome: 保存执行结果或出现异常时保存异常信息
4.runner:执行task的线程,在取消和中断线程时需要知道是哪个线程。
5.waiters:为WaitNode 的对象。WaitNode 为一个简单的单向链表结构的队列,当任务执行过程中,有线程想要调用get()获取结果时,则该线程会被阻塞加入WaitNode,直到该任务执行完毕。
以下为WaitNode的结构:

static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

仅由一个Thread和指向下一个节点的next组成,thread即为当前线程。其本质是一个Treiber栈。

3.2静态代码块中初始化需要CAS操作的属性的偏移量

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

CAS操作调用的是compareAndSwap***方法

3.3 构造方法

3.3.1

 public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

将传入的callable赋值给上面说的成员变量callable,将state初始化为NEW;
3.3.2

 public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

传入Runnable对象,通过Executors.callable()方法将runnable转为callable保存给callable变量,同时会返回传入的result。
将runnable转为callable的方法如下:

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

这里调用RunnableAdapter()适配,代码如下:

static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }
 }

RunnableAdapter实现了Callable接口,在call()中调用task.run(),将传入的result返回。

3.4 方法

3.4.1 run()
public void run() {
		//代码1
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                //代码2
                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()
            //代码3
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

代码1:
1.state!=NEW: 如果state不为NEW,即任务已经执行完毕或被取消,直接返回。
2. 若state是NEW,则尝试将当前线程保存到runner中,即如果runner为空就将当前线程保存给runner,若runner不为空,则说明已经有线程在执行,直接return,否则进入try-catch。

代码2:
1.定义V result保存callable的执行结果,置ran为true,代表执行成功。
2.若出现异常,则在catch中捕获,将result置为null,ran置为false代表出现异常,同时通过setException(ex)设置异常。
setException(ex)代码如下:

protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

此方法中,尝试将state的值从NEW改为COMPLETING,如果成功,则将异常信息保存到outcome中,然后再将状态改为最终态EXCEPTIONAL,最后调用finishCompletion()移除并唤醒WaitNode中所有阻塞等待的线程,调用done(),并让callable无效,源码如下:

private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        done();
        callable = null;   
    }
  1. 如果ran为true,即正常执行,通过set(result),set()源码如下:
protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

在set()中,尝试将状态从NEW变为COMPLETING中间态(这里再重新理解上面说的state的7个状态会更深刻一点),并将结果保存给outcome,接着将state由COMPLETING转为NORMAL最终状态,同上调用 finishCompletion()

代码3
1.在finally中重新将runner置空,表明当前线程运行完毕,在下一个线程进入到run()(代码1)方法时,便可通过CAS重新将线程保存到runner
2.若ran为false,即出现了异常,则调用handlePossibleCancellationInterrupt(state)处理异常,

private void handlePossibleCancellationInterrupt(int s) {
        // It is possible for our interrupter to stall before getting a
        // chance to interrupt us.  Let's spin-wait patiently.
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt

        // assert state == INTERRUPTED;

        // We want to clear any interrupt we may have received from
        // cancel(true).  However, it is permissible to use interrupts
        // as an independent mechanism for a task to communicate with
        // its caller, and there is no way to clear only the
        // cancellation interrupt.
        //
        // Thread.interrupted();
    }

在该方法中,如果状态一直是INTERUPING,则一直进行yield()。

3.4.2 get()

线程可调用get()方法获取任务执行的结果,如果当前任务尚未执行完毕,则阻塞调用get()方法的线程,直到任务执行结束。

/**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

get()方法中首先判断当前的状态是否小于等于COMPLETING,即任务是否处于尚未开始或尚未结束或结果尚未保存到outcome的状态,如果是,则调用awaitDone(false, 0L);阻塞等待,否则调用report(s)返回执行结果。

report()
 private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

获取当前结果outcome,如果当前状态为NORMAL,则直接返回结果,如果当前状态大于等于CANCELLED,则抛出CancellationException()异常,其他状态则抛出ExecutionException()异常。

3.4.3 awaitDone()

线程调用此方法阻塞等待任务执行结束。

    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        //若开启超时,则计算截止时间
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        //创建一个WaitNode节点
        WaitNode q = null;
        //是否进入阻塞队列
        boolean queued = false;
        for (;;) {
        	//代码1
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }
			
            int s = state;
            //代码2
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            //代码3
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            //代码4
            else if (q == null)
                q = new WaitNode();
            //代码5
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            //代码6
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

方法的参数为 timed(是否开启超时时间)和nanos(若开启超时时间,则此参数为超时时间)
(代码1到代码6在一个无限for循环中,不停的进行循环判断)

代码1:
如果当前线程被中断,则将阻塞队列waitNode中的该节点给移除,并抛出InterruptedException()异常。
代码2:
判断当前阻塞线程的状态是否大于COMPLETING,如果大于,则该任务已经执行完毕,如果当前节点非空,则删除该节点的thread,并返回状态。
代码3: 如果当前状态为COMPLETING,则说明任务执行完毕,但是执行结果尚未赋值给outcome,此时执行yield让出CPU。
代码4:如果当前阻塞等待节点q为空,则新建一个节点。
**代码5:**如果尚未入队列,则将上一个节点的next指向waiters,然后尝试让新的节点q替换waiters,如果尝试成功则queued为true,否则为false。
**代码6:**如果开启了超时限制,则重新计算等待时间,如果 nanos<=0L,即超时了,则移除等待队列中的该节点,并返回当前state;如果没有超时,则继续阻塞重新计算后的时间。如果没有开启超时限制,则直接阻塞等待直到被唤醒。

3.4.4 cancel()

public boolean cancel(boolean mayInterruptIfRunning) {
		//代码1
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try { 
        	//代码2
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
        	//代码3
            finishCompletion();
        }
        return true;
    }

代码1:
1.如果state不等于NEW,则任务已经执行完毕或出现异常或被取消,直接返回false
2.如果任务尚未开始,则通过CAS尝试将状态从NEW改为mayInterruptIfRunning,若mayInterruptIfRunningtrue,代表需要中断任务执行线程,则改为INTERRUPTING中间状态,否则改为CANCELLED最终状态,如果尝试更新成功,则返回true,否则返回false
代码2:
如果进行了中断,则获取当前的线程runner,中断之。最后将状态由INTERRUPTING改为INTERRUPTED
代码3:
执行finishCompltion()(任务出现异常或正常执行完毕或成功取消任务都会调用该方法,上文已提到),返回true

3.4.5 isCancelled()

public boolean isCancelled() {
        return state >= CANCELLED;
    }

判断任务是否被取消。只要状态大于等于CANCELLED都返回true。

3.4.6 isDone()

public boolean isDone() {
        return state != NEW;
    }

只要不是NEW,都返回true,代表任务已完成。

4.Callable+Future示例

package com.wml.test2.future;

import java.util.concurrent.*;

/**
 * @author MaoLin Wang
 * @date 2020/1/2723:18
 */
public class FutureDemo {

    public static class Task implements Callable<Integer>{

        private int sum;
        @Override
        public Integer call() throws Exception {
            System.out.println("Callable线程开始执行任务");
            Thread.sleep(1500);
            for (int i = 0; i < 50; i++) {
                if (Thread.currentThread().isInterrupted()){
                    System.out.println("Callable线程计算任务中断");
                    return null;
                }
                if (i%2==0){
                    sum=sum+i;
                    System.out.println("sum="+sum);
                }

            }
            System.out.println("Callable线程执行完毕 计算结果为"+sum);
            return sum;
        }
    }

    public static void main(String[] args) {
        Task task=new Task();

        ExecutorService pool = new ThreadPoolExecutor(6,100,0L,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
        Future<Integer>future=pool.submit(task);
        pool.shutdown();

        try {
            Thread.sleep(3000L);
            System.out.println("主线程执行其他任务中.....");
            if (future.get()!=null){
                System.out.println("主线程获取Callable执行结果:"+future.get());
            }else {
                System.out.println("尚未获取到结果");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}


结果:

Callable线程开始执行任务
sum=0
sum=2
sum=6
sum=12
sum=20
sum=30
sum=42
sum=56
sum=72
sum=90
sum=110
sum=132
sum=156
sum=182
sum=210
sum=240
sum=272
sum=306
sum=342
sum=380
sum=420
sum=462
sum=506
sum=552
sum=600
Callable线程执行完毕 计算结果为600
主线程执行其他任务中.....
主线程获取Callable执行结果:600

五、Callable+FutureTask示例

Task类同上

  public static void main(String[] args) {
        ExecutorService pool = new ThreadPoolExecutor(6,100,0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());

        Task task=new Task();
        //创建FutureTask任务
        FutureTask futureTask=new FutureTask(task);
        pool.submit(futureTask);
        pool.shutdown();
        try {
            Thread.sleep(3000L);
            System.out.println("主线程执行其他任务中.....");
            if (futureTask.get()!=null){
                System.out.println("主线程获取Callable执行结果:"+futureTask.get());
            }else {
                System.out.println("尚未获取到结果");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

结果:

Callable子线程开始计算!
sum=0
sum=1
sum=3
sum=6
sum=10
sum=15
sum=21
Cancel................. 
sum=28
Callable子线程计算任务中断!

(如有错误还请指正~感谢)

发布了75 篇原创文章 · 获赞 13 · 访问量 8369

猜你喜欢

转载自blog.csdn.net/weixin_43696529/article/details/104096600