J.U.C之Future

Future

1. Callable介绍

Executor框架使用Runnable作为其基本的任务表示形式。Runnable是一种有很大局限的抽象,虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但它不能返回一个值或抛出一个受检查的异常。

许多任务实际上都是存在延迟的计算——执行数据库查询,从网络上获取资源,或者计算某个复杂的功能。对于这些任务,Callable是一种更好的抽象:他认为主入口点将返回一个值,并可能抛出一个异常。

Runnable和Callable描述的都是抽象的计算任务。这些任务通常都是有范围的,即都有一个明确的起始点,并且最终会结束。Executor执行的任务有4个生命周期阶段:创建、提交、开始、完成。由于有些任务可能要执行很长的时间,因此通常希望能够取消这些任务。在Executor框架中,已提交但尚未开始的任务可以取消,但对于那些已经开始执行的任务,只有当它们能相应中断时,才能取消。取消一个已完成的任务不会有任何影响。

2. Future介绍

Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果或取消任务等。

Get方法的行为取决于任务的状态。如果任务已经完成,get会立即返回或者抛出异常;如果任务没有完成,那么get将阻塞直到任务完成。如果任务抛出了异常,get将该异常封装为ExecutionException并重新抛出。如果任务被取消,那么get将抛出CancellaionException。

实例:

package com.mylearn.thread.future;

import com.sun.org.apache.xpath.internal.functions.FuncTrue;

import java.util.concurrent.*;

/**

 * Created by IntelliJ IDEA.

 * User: yingkuohao

 * Date: 13-7-15

 * Time: 下午4:21

 * CopyRight:360buy

 * Descrption:

 * To change this template use File | Settings | File Templates.

 */

public class  FutureTest {

    public static void main(String args[]) {

        ExecutorService executorService = Executors.newCachedThreadPool();

        //submitTest(executorService);   //可以用submit

        futureTaskTest(executorService); //也可以用execute

    }

    /**

     *  FutureTask是Future的一个实现,Future还实现了Runnable,所以可通过Executor来执行。

     * @param executorService

     */

    private static void futureTaskTest(ExecutorService executorService) {

        FutureTask futureTask = new FutureTask(new Callable() {

            public Object call() throws Exception {

                System.out.println("dosomething");

                            return "ok";  //To

            }

        }

        );

        executorService.execute(futureTask);

    }

    private static void submitTest(ExecutorService executorService) {

        Future future = executorService.submit(new Callable() {

            public Object call() throws Exception {

                System.out.println("dosomething");

                return "ok";  //To change body of implemented methods use File | Settings | File Templates.

            }

        });

        try {

            Object obj = future.get();

            System.out.println(obj.toString());

        } catch (InterruptedException e) {

            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

        } catch (ExecutionException e) {

            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

        }

    }

}

3. FutureTask

FutureTask是Future的一个实现类。

实例:

package com.mylearn.thread.future;

import java.util.Random;

import java.util.concurrent.Callable;

/**

 * Created by IntelliJ IDEA.

 * User: yingkuohao

 * Date: 13-8-14

 * Time: 上午10:58

 * CopyRight:360buy

 * Descrption:

 * To change this template use File | Settings | File Templates.

 */

public class CountNum implements Callable {

    private Integer max;

    public CountNum(Integer max) {

        this.max = max;

    }

    public Integer call() throws Exception {

        System.out.println("执行call方法");

        Random random = new Random();

        int i = random.nextInt(max);

        System.out.println("获得随机数i=" + i);

//        Thread.sleep(1000*200);

//        System.out.println("CountNum线程睡眠一秒");

        return i;

    }

}

FutureTaskTest

package com.mylearn.thread.future;

import java.util.concurrent.*;

/**

 * Created by IntelliJ IDEA.

 * User: yingkuohao

 * Date: 13-8-14

 * Time: 上午11:01

 * CopyRight:360buy

 * Descrption:  如果不想分支线程阻塞主线程,又想要取得分支线程的执行结果,就用futureTask

 * To change this template use File | Settings | File Templates.

 */

public class FuterTaskTest {

    public static void main(String args[]) {

        CountNum countNum = new CountNum(100);    //new一个callable实现类

        FutureTask<Integer> futureTask = new FutureTask<Integer>(countNum);//new一个futureTask

        Thread maiThread = new Thread(futureTask);    //由于futureTask也实现了Runnable,所以可以直接new Thread 来启动。更多的

        System.out.println("主线程启动");

        maiThread.start();   //启动子线程

 /*       //当然也可以用线程池

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        Future future = executorService.submit(futureTask);  //有返回值

        executorService.execute(futureTask);  //无返回值

*/

        long begin = System.nanoTime();

    /*    try {

            Thread.sleep(4000); //设为4秒时,子线程是3秒,总时间是4秒

            Thread.sleep(1000);//设为1秒的时候,子线程是3秒,总时间是3秒,由此可见,主子线程异步执行,时间取决于最长时间

        } catch (InterruptedException e) {

            e.printStackTrace();

        }*/

        System.out.println("主线程dosomthing");      //主线程做自己的工作,不影响

        try {

            Integer  result = futureTask.get();   //get方法,如果子线程没返回会阻塞,直至返回或者抛出异常

            System.out.println(" maiThread.interrupted"+ maiThread.interrupted());

            System.out.println(" Thread.interrupted"+ Thread.currentThread().interrupted());

            System.out.println("获取结果:" +result);

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

        long end = System.nanoTime();

        System.out.println(String.format("执行总时间:%dns" , (end - begin)));

    }

}

4. FutureTask源码

1. FutureTask.get()

    public V get() throws InterruptedException, ExecutionException {

        return sync.innerGet();

    }

2. Sync. innerGet()

        V innerGet() throws InterruptedException, ExecutionException {

            acquireSharedInterruptibly(0); //获取锁,若不成功,阻塞

            if (getState() == CANCELLED) //如果状态返回值是取消,抛异常

                throw new CancellationException();

            if (exception != null) //如果返回异常,重新封装异常,抛出

                throw new ExecutionException(exception);

            return result; // FutureTask的run方法会设置result。

        }

2.1 acquireSharedInterruptibly

    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {

        if (Thread.interrupted()) /如果线程被中断,抛出异常

            throw new InterruptedException();

        if (tryAcquireShared(arg) < 0) //尝试获取锁

            doAcquireSharedInterruptibly(arg);

    }

 2.1.1 tryAcquireShared

        protected int tryAcquireShared(int ignore) {

            return innerIsDone()? 1 : -1; //重写AQS的方法,返回是否已经完成

        }

3. AQS.doAcquireSharedInterruptibly

    private void doAcquireSharedInterruptibly(int arg)

        throws InterruptedException {

        final Node node = addWaiter(Node.SHARED);

        try {

            for (;;) {

                final Node p = node.predecessor();

                if (p == head) {

                    int r = tryAcquireShared(arg); //尝试获取锁

                    if (r >= 0) {

                        setHeadAndPropagate(node, r);

                        p.next = null; // help GC

                        return;

                    }

                }

                if (shouldParkAfterFailedAcquire(p, node) &&

                    parkAndCheckInterrupt())//核心代码:判断是否需要阻塞

                    break;

            }

        } catch (RuntimeException ex) {

            cancelAcquire(node);

            throw ex;

        }

        // Arrive here only if interrupted

        cancelAcquire(node);

        throw new InterruptedException();

}

通过3.1的解析我们调试此段代码,第一次进入的时候shouldParkAfterFailedAcquire会返回fasle,因为waitStatus=0,第二次轮询时,waitStatus=SIGNAL,这时候返回true,然后进入parkAndCheckInterrupt代码,parkAndCheckInterrupt就是执行线程阻塞的逻辑,这样,FutureTask的get方法就实现了阻塞。

3.1.AQS. shouldParkAfterFailedAcquire()

   /**

     * Checks and updates status for a node that failed to acquire.

     * Returns true if thread should block. This is the main signal

     * control in all acquire loops.  Requires that pred == node.prev

     *

     * @param pred node's predecessor holding status

     * @param node the node

     * @return {@code true} if thread should block

     */

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

        int s = pred.waitStatus; //默认情况下节点的waitStatus为0

        if (s < 0)

            /*

             * This node has already set status asking a release

             * to signal it, so it can safely park,节点可以阻塞了

             */

            return true;

        if (s > 0) {

            /*

             * Predecessor was cancelled. Skip over predecessors and

             * indicate retry.节点取消

             */

    do {

node.prev = pred = pred.prev;

    } while (pred.waitStatus > 0);

    pred.next = node;

}

        else

            /*

             * Indicate that we need a signal, but don't park yet. Caller

             * will need to retry to make sure it cannot acquire before

             * parking. 通过CAS把pred节点的waitStatus改为signal,意思是

这个节点(线程)可以阻塞了,但是现在还没有阻塞,相当于就是一个中间状态,下次check的时候就会阻塞此线程。

             */

            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);

        return false;

}

说明:waitStatus有以下几种状态:

/** waitStatus value to indicate thread has cancelled表示线程已经取消 */

        static final int CANCELLED =  1; 

        /** waitStatus value to indicate successor's thread needs unparking 标记successor's 线程需要阻塞,这个感觉注释不准,主要就是一个标记*/

        static final int SIGNAL    = -1;

        /** waitStatus value to indicate thread is waiting on condition ,标记线程处于等待状态*/

        static final int CONDITION = -2;

3.AQS.parkAndCheckInterrupt

    private final boolean parkAndCheckInterrupt() {

        LockSupport.park(this);

        return Thread.interrupted();

    }

参考:http://yhjhappy234.blog.163.com/blog/static/3163283220135875759265/

http://hi.baidu.com/gefforey520/item/18c19fa7c78466268819d307

FutureTask中AQS主要用于保存任务的状态,如正在运行、已完成、已取消。

如:

        protected int tryAcquireShared(int ignore) {

            return innerIsDone()? 1 : -1;

        }

        boolean innerIsDone() {

            return ranOrCancelled(getState()) && runner == null;

        }

        private boolean ranOrCancelled(int state) {

            return (state & (RAN | CANCELLED)) != 0;

        }

        /** State value representing that task is running */

        private static final int RUNNING   = 1;

        /** State value representing that task ran */

        private static final int RAN       = 2;

        /** State value representing that task was cancelled */

        private static final int CANCELLED = 4;

5. CompletionService介绍

    将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者submit执行的任务。使用者take已完成的任务,并按照完成这些任务的顺序处理他们的结果。

实例:

package com.mylearn.thread.future;

import com.mylearn.thread.future.CountNum;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.*;

/**

 * Created by IntelliJ IDEA.

 * User: yingkuohao

 * Date: 13-8-14

 * Time: 下午2:24

 * CopyRight:360buy

 * Descrption:

 * 此类将安排那些完成时提交的任务,把他们放置在可使用take访问的队列上。

 * To change this template use File | Settings | File Templates.

 */

public class CompletionServiceTest {

    public static void main(String args[]) {

        //new一个callable实现类

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executorService);//传入一个executor

//        getResult(completionService);

        getFirstResult(completionService);

    }

    /**

     * 获取第一个值后退出,让其他线程停止

     * @param completionService

     */

    private static void getFirstResult(CompletionService<Integer> completionService) {

        List<Future<Integer>> lst = new ArrayList<Future<Integer>>();

        Integer finalResult = null;

        //开50个任务

        for (int i = 100; i < 150; i++) {

            CountNum countNum = new CountNum(i);

            Future<Integer> curFuture = completionService.submit(countNum);    //提交,封装callabe为一个FutureTask,相当于还是传入的executor去执行这个FutureTask,然后把返回值放入一个BlockingQueue

            lst.add(curFuture);

        }

        for (int j = 0; j < 50; j++) {

            try {

                Integer result = completionService.take().get();   //获取第一个结果

                //如果第一个结果不为空,取值,直接退出

                if (result != null) {

                    finalResult = result;

                    break;

                }

            } catch (InterruptedException e) {

                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

            } catch (ExecutionException e) {

                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

            }

        }

        //取到第一个结果后,把其他的任务取消。

        for (Future<Integer> future : lst) {

            future.cancel(true);

        }

        if (finalResult != null) {

            System.out.println("finalResult =" + finalResult);

        }

    }

    private static void getResult(CompletionService<Integer> completionService) {

        for (int i = 100; i < 150; i++) {

            CountNum countNum = new CountNum(i);

            completionService.submit(countNum);    //提交,封装callabe为一个FutureTask,相当于还是传入的executor去执行这个FutureTask,然后把返回值放入一个BlockingQueue

        }

        for (int i = 0; i < 50; i++) {

            try {

                Integer r = completionService.take().get();  //获取返回值

                System.out.println("r= " + r);

            } catch (InterruptedException e) {

                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

            } catch (ExecutionException e) {

                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.

            }

        }

    }

}

6. FuturesTask使用,缓存

参考另一篇文章《缓存思想分析》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值