[Java多线程] Future及 Callable的使用解析

前言

我们一直都知道, Java内的多线程主要有2种方式. 同步方式和异步方式. 同步方式, 我们一般是使用Runnbale接口; 异步方式, 我们一般使用的是Callable接口.

我们今天就来详细的看一下这2个接口, 以及相关的使用和实现.


相关接口

  • Runnable

作为Java实现多线程的入门级接口. 相信大家都不会非常陌生. 其相关实现, 如下所示:

/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.lang;

/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   JDK1.0
 */
@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();
}

可以看到. 这个接口的定时是1994年, 在Java 1.0的时候就已经定义好了.

使用这个接口也非常简单. 只需要实现这个接口, 随后将其塞入一个实际的线程中即可. 示例代码如下所示:

package com.yanxml.arsenal.multi;

/**
 * Runnable 实现.
 * @author sean
 * @date 2021-11-28 00:00
 */
public class RunnableTest {

    /**
     * 示例代码1: 使用Runnable.
     */
    public static void testMethod1(){
        Runnable startRunningTask  = new StartRunning();
        Thread runThread = new Thread(startRunningTask);
        runThread.start();
    }

    /**
     * 示例代码2: 匿名函数写法.
     */
    public static void testMethod2(){
        Thread runThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Start Running...");
            }
        });
        runThread.start();
    }

    /**
     * 示例代码3: lambda表达式写法
     */
    public static void testMethod3(){
        Thread runThread = new Thread(() -> System.out.println("Start Running Lambda."));
        runThread.start();
    }

    public static void main(String[] args) {
        // 基础Running 接口写法.
        testMethod1();
        // 匿名函数写法
        testMethod2();
        // lambda表达式写法
        testMethod3();
    }

}

class StartRunning implements Runnable{

    @Override
    public void run() {
        System.out.println("Start Running.");
    }
}

用lambda表达式实现Runnable


Callable & Future

当我们进行在Java内需要使用异步操作时, 就经常会使用到CallableFuture. 我们仔细了解下这2个接口的相关实现.

  • Callable
/*
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

package java.util.concurrent;

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code 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;
}

可以看到Callable接口是JDK 1.5才开始定义并且开始使用的. 其中也是只定义了一个接口, 与Runnable不同的是, 其返回值为V, 而Runnable的返回值为void. 即是可以获取到返回值的.

  • FutureTask & Callable的相关使用

由于在Java内, 不论是单线程还是线程池. 基本上只认ThreadRunnable接口. 所以, 我们在使用Callable的时候. 也还是都需要再做一层的封装的.

# RunnableFuture
package java.util.concurrent;

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the {@code run} method causes completion of the {@code Future}
 * and allows access to its results.
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's {@code get} method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

RunnableFuture. RunnableFuture2个接口的结合体.

public class FutureTask<V> implements RunnableFuture<V> {
	    public void run() {
	    // 当前Thread状态是否是New状态
	    # 当前线程是否是Thread.currentThread() 即是否轮到当前线程进行执行
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                // ran用来记录是否执行成功
                boolean ran;
                try {
                	// 调用callable接口实现. 并获取返回值.
                    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()
            # 方便JVM回收
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
}
  • 由上可以看到. 我们使用了Runnable接口方法, 又包了一层, 用于方便外层调用. 其中, 还是调用了Callable接口的call()方法进行实现的.

Others

由于时间原因. 目前此处只介绍下run方法. 稍后再补充下其他几个方法. cancel()get()方法.

猜你喜欢

转载自blog.csdn.net/u010416101/article/details/121600269