美文网首页
FutureTask源码解析(1)

FutureTask源码解析(1)

作者: gmdqtd | 来源:发表于2020-01-11 18:50 被阅读0次
    目录

    1 概述
    2  FutureTask所使用到的接口
     2.1 Future接口
    3  FutureTask所使用到的类
     3.1 Executor类
     3.2 UnSafe类


    1 概述

    FutureTask是一个任务可取消的任务异步计算类,该类实现Future接口,实现了启动与取消任务的方法。当计算完成后,通过get方法可以获取计算结果,get方法是个block方法,直接到任务完成才能返回计算结果。

    2 FutureTask所使用到的接口

    2.1 Future接口

    Future接口被设计用来代表一个异步操作的执行结果。你可以用它来获取一个操作的执行结果、取消一个操作、判断一个操作是否已经完成或者是否被取消,获取执行结果只能通过get方法获取,get方法是一个阻塞方法,会等待计算完成返回执行结果。

    public interface Future<V> {
        V get() throws InterruptedException, ExecutionException;
        V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
        
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled();
        
        boolean isDone();
    }
    

    Future接口一共定义了5个方法:

    • get()
      该方法用来获取执行结果, 如果任务还在执行中, 就阻塞等待;
    • get(long timeout, TimeUnit unit)
      该方法同get方法类似, 所不同的是, 它最多等待指定的时间, 如果指定时间内任务没有完成, 则会抛出TimeoutException异常;
    • cancel(boolean mayInterruptIfRunning)
      该方法用来尝试取消一个任务的执行, 它的返回值是boolean类型, 表示取消操作是否成功。

    有以下三种情况之一的,cancel操作一定是失败的:
    1、任务已经执行完成了
    2、任务已经被取消过了
    3、任务因为某种原因不能被取消
    其它情况下,cancel操作将返回true。值得注意的是,cancel操作返回true并不代表任务真的就是被取消了,这取决于发动cancel状态时任务所处的状态:
    1、如果发起cancel时任务还没有开始运行,则随后任务就不会被执行;
    如果发起cancel时任务已经在运行了,则这时就需要看mayInterruptIfRunning参数了:

    2、如果mayInterruptIfRunning 为true, 则当前在执行的任务会被中断
    3、如果mayInterruptIfRunning 为false, 则可以允许正在执行的任务继续运行,直到它执行完。

    • isCancelled()

    该方法用于判断任务是否被取消了。如果一个任务在正常执行完成之前被cancel掉了, 则返回true

    • isDone()

    如果一个任务已经结束, 则返回true。注意, 这里的任务结束包含了以下三种情况:

    1. 任务正常执行完毕
    2. 任务抛出了异常
    3. 任务已经被取消

    3 FutureTask使用到的类


    3.1 Executors
    3.2 Unsafe

    Unsafe类对于并发编程来说是个很重要的类,J.U.C里的源码(你会发现到处充斥着这个类的方法调用。这个类的最大的特点在于,它提供了硬件级别的CAS原子操作。

    CAS可以说是实现了最轻量级的锁,当多个线程尝试使用CAS同时更新同一个变量时,只有其中的一个线程能成功地更新变量的值,而其他的线程将失败。然而,失败的线程并不会被挂起。

    CAS操作包含了三个操作数:需要读写的 内存位置,进行比较的原值,拟写入的新值

    在Unsafe类中,实现CAS操作的方法是: compareAndSwapXXX

    例如:

    public native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update);
    
    • obj是我们要操作的目标对象
    • offset表示了目标对象中,对应的属性的内存偏移量
    • expect是进行比较的原值
    • update是拟写入的新值。

    所以该方法实现了对目标对象obj中的某个成员变量(field)进行CAS操作的功能。那么,要怎么获得目标field的内存偏移量offset呢? Unsafe类为我们提供了一个方法:

    public native long objectFieldOffset(Field field);
    

    该方法的参数是我们要进行CAS操作的field对象,要怎么获得这个field对象呢?最直接的办法就是通过反射了:

    Class<?> k = FutureTask.class;
    Field stateField = k.getDeclaredField("state");
    

    我们就能对FutureTask的state属性进行CAS操作

    除了compareAndSwapObject,Unsafe类还提供了更为具体的对int和long类型的CAS操作:

    public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);
    public native boolean compareAndSwapLong(Object obj, long offset, long expect, long update);
    

    从方法签名可以看出,这里只是把目标field的类型限定成int和long类型,而不是通用的Object.

    最后,FutureTask还用到了一个方法:

    public native void putOrderedInt(Object obj, long offset, int value);
    

    可以看出,该方法只有三个参数,所以它没有比较再交换的概念,某种程度上就是一个赋值操作,即设置obj对象中offset偏移地址对应的int类型的field的值为指定值,但不保证值的改变被其他线程立即看到,只有在field被\color{red}{volatile}修饰并且期望被意外修改的时候使用才有用。
    与putOrderedInt方法相似的方法\color{red}{putOrderedInt}

    public native void putIntVolatile(Object obj, long offset, int value);
    

    该方法设置obj对象中offset偏移地址对应的整型field的值为指定值,支持volatile变量可见性。由此可以看出,当操作的int类型field本身已经被volatile修饰时,putOrderedInt和putIntVolatile是等价的。

    相关文章

      网友评论

          本文标题:FutureTask源码解析(1)

          本文链接:https://www.haomeiwen.com/subject/azcvactx.html