Thread、Runnable、Callable、Future、FutureTask之间的关系

Thread 和 Runnable

Thread

public class MyThread extends Thread {

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "开始运行");
    }

    public static void main(String[] args) {
        new MyThread("testThread").start();
    }
}

线程包含 4 个状态:创建 -> 就绪 -> 运行 -> 结束。

当执行 start() 后,线程进入就绪状态,当对应的线程抢占到 cpu 调度资源之后,进入运行状态,此时调用的是 run 方法,执行完毕之后就结束了。


Runnable

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "已经运行");
    }

    public static void main(String[] args) {
        new Thread(new MyRunnable(),"testRunnable").start();
    }
}

这里 MyRunnable就是一个 Runnable,实现了 run() 方法,作为 Thread() 的入参。


Thread 和 Runnable 的关系
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

大致意思如下:当一个对象继承并实现了 run() 方法,当线程 start() 后,会在该线程中单独执行该对象的 run() 方法。

这段翻译,基本就告诉了 Runnable 和 Thread 的关系:

  1. MyRunnable继承 Runnable,并实现了 run() 方法;
  2. Thread 初始化,将 MyRunnable作为自己的成员变量;
  3. Thread 执行 run() 方法,线程处于“就绪”状态;
  4. 等待 CPU 调度,执行 Thread 的 run() 方法,但是 run() 的内部实现,其实是执行的 MyRunnable.run() 方法,线程处于“运行”状态。

在 Thread 初始化时,MyRunnable作为入参 target,最后赋值给 Thread.target

new Thread(new MyRunnable(),"testRunnable").start();
--->
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
--->
        private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
--->
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
		....
        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        //将传入的MyRunnable赋值给target
        this.target = target;
    ...

    }

当执行 Thread.run() 时,其实是执行的 target.run(),即 MyRunnable.run(),这个是典型的策略模式

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

Callable 、Future 和 FutureTask

其实对于 Thread 和 Runable,其 run() 都是无返回值的,并且无法抛出异常,所以当你需要返回多线程的数据,就需要借助 Callable 和 Future。


Callable

Callable 是一个接口,里面有个 V call() 方法,这个 V 就是我们的返回值类型

@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;
}

在这里插入图片描述

可以看到,Thread的构造方法里面并没有Callable的入参

那么这里有个问题,callable.call()是如何被调用的呢,这个 callable.call() 和 Thread.run() 是什么关系呢?


FutureTask

public class FutureTask<V> implements RunnableFuture<V> {
--->
    public interface RunnableFuture<V> extends Runnable, Future<V> {

所以,FutureTask 也是个 Runnable

既然 FutureTask 是个 Runnable,肯定就需要实现 FutureTask.run() 方法,那么 FutureTask 也可以作为 Thread 的初始化入参

new Thread(FutureTask对象).start();

所以当执行 Thread.run() 时,其实是执行的 FutureTask.run()


Callable 和 FutureTask 的关系

在这里插入图片描述

可以看到FutureTask的入参,包括Callable作为初始化参数

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

当执行 FutureTask.run() 时,其实执行的是 Callable.call()

    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 {
                    //执行核心业务逻辑
                    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()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

所以,这里又是一个典型的策略模式 !

现在我们应该可以很清楚知道 Thread 、Runnable、FutureTask 和 Callable 的关系:

  • Thread.run() 执行的是 Runnable.run();
  • FutureTask 继承了 Runnable,并实现了 FutureTask.run();
  • FutureTask.run() 执行的是 Callable.call();
  • 依次传递,最后 Thread.run(),其实是执行的 Callable.call()。

所以整个设计方法,其实就是 2 个策略模式,Thread 和 Runnable 是一个策略模式,FutureTask 和 Callable 又是一个策略模式,最后通过 Runnable 和 FutureTask 的继承关系,将这 2 个策略模式组合在一起。


Future

为什么要有 Future 呢?我再问一个问题,大家可能就知道了。

我们通过 FutureTask,借助 Thread 执行线程后,结果数据我们怎么获取到呢?这里就需要借助到 Future。

public interface Future<V> {
    // 取消任务,如果任务正在运行的,mayInterruptIfRunning为true时,表明这个任务会被打断的,并返回true;
    // 为false时,会等待这个任务执行完,返回true;若任务还没执行,取消任务后返回true,如任务执行完,返回false
    boolean cancel(boolean mayInterruptIfRunning);
    // 判断任务是否被取消了,正常执行完不算被取消
    boolean isCancelled();
    // 判断任务是否已经执行完成,任务取消或发生异常也算是完成,返回true
    boolean isDone();
    // 获取任务返回结果,如果任务没有执行完成则等待完成将结果返回,如果获取的过程中发生异常就抛出异常,
    // 比如中断就会抛出InterruptedException异常等异常
    V get() throws InterruptedException, ExecutionException;
    // 在规定的时间如果没有返回结果就会抛出TimeoutException异常
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

对于 FutureTask,Callable 就是他的任务,而 FutureTask 内部维护了一个任务状态,所有的状态都是围绕这个任务来进行的,随着任务的进行,状态也在不断的更新。

FutureTask 继承了 Future,实现对任务的取消、数据获取、任务状态判断等功能。

比如我们经常会调用 get() 方法获取数据,如果任务没有执行完成,会将当前线程放入阻塞队列等待,当任务执行完后,会唤醒阻塞队列中的线程。


Callable的使用

通过FutureTask,交给线程池执行,阻塞等待结果返回

public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "call";
        }
    });
    executorService.submit(futureTask);
    System.out.println(futureTask.get());
}

通过线程池执行,返回Future对象

public static void main(String[] args) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    Future<String> submit = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "call";
        }
    });
    System.out.println(submit.get());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值