zoukankan      html  css  js  c++  java
  • FutureTask的理解

    我们都知道实现线程的方式有三种,分别是继承Thread类、实现Runnable接口和实现Callable接口。大致说一下使用场景:

    • 继承Thread类:如果类已经继承了其他接口,就不能通过此种方式实现线程了
    • 实现Runnable接口:不需要返回结果的线程
    • 实现Callable接口:需要返回结果

    线程返回值是通过FutureTask拿到的结果,下面是FutureTask类的继承关系:

    下面通过一个例子说明一下怎么获取线程的执行结果:

    //定义一个类,实现Callable接口
    public
    class MyCallable implements Callable { @Override public Object call() throws Exception {
         //睡眠5秒中,模拟任务的执行 TimeUnit.SECONDS.sleep(
    5);
         //返回的结果值
    return "5s OK"; } }
    public class Main {
    
        public static void main(String[] args) throws Exception{
    
            System.out.println("----------main start----------");
    
            //通过构造方法传入实现Callable接口的实例
            //构造方法也可以传入实现Runnable接口的实例,最终还是适配器转换成了Callable
            FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
    
            //FutureTask最终还是一个实现了Runnable接口的类,最终还是需要开启一个线程去执行任务
            new Thread(futureTask).start();
    
            //get是阻塞方法,取到值之前会一直阻塞
            String value = futureTask.get();
            System.out.println("thread result:" + value);
    
            System.out.println("----------main end----------");
        }
    }

    最终的执行结果:

    源码分析

     //Thread中的start方法
    public synchronized void start() { //判断线程状态,不是新建状态就抛出异常 if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; //调用本地方法,开启一个线程 start0(); started = true; } //本地方法 private native void start0();

    从源码可以看出调用Thread.start()方法后会调用本地的start0()方法开启线程,具体执行逻辑是在run()方法中。

    //Thread中的run方法调用了实现Runnable接口的run()方法
    @Override
    public void run() { if (target != null) { target.run(); } }

    从类图中可以看到FutureTask是实现了Runnable接口的类,所以Thread中的run()方法最终执行的是FutureTask中的run()方法,下面是源码:

    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 {
                //调用Callable中的call方法 result
    = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { runner = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }

    其实FutureTask还是比较简单,Callable就是他的任务,而FutureTask内部维护了一个任务状态,所有的状态都是围绕这个任务来进行的,随着任务的进行,状态也在不断的更新。任务发起者调用get()方法时,如果任务没有执行完成,会将当前线程放入阻塞队列等待,当任务执行完后,会唤醒阻塞队列中的线程。

    “小智”和“佩奇”是同班同学,佩奇同学非常乐于思考,经常提问题,小智同学,聪明绝顶,乐于解答问题。
  • 相关阅读:
    SSL测试百度握手协议,下载百度CA证书
    新浪微博推荐之股神一探究竟,是大神?
    IPQ4019开发板使用 openWRT开发(第2篇)未完成!!!
    IPQ4019开发板使用硬件和启动(第一篇)
    2020-01-17 学习笔记(1)
    Kube-DNS搭建(1.4版本)
    Kubernetes持久化存储2——探究实验
    Kubernetes持久化存储1——示例
    Kubernetes部分Volume类型介绍及yaml示例
    Kubernetes外挂配置管理—ConfigMap介绍
  • 原文地址:https://www.cnblogs.com/liyefei/p/13415414.html
Copyright © 2011-2022 走看看