【Javaマルチスレッド】FutureとCallableの利用分析

序文

Java のマルチスレッドには、同期方式と非同期方式の 2 つの主な方式があることは以前から知られていますが、同期方式では通常Runnbaleインターフェイスが使用され、非同期方式では通常Callableインターフェイスが使用されます。

今日は、これら 2 つのインターフェイスと、それらに関連する使用法と実装について詳しく説明します。


関連するインターフェース

  • 実行可能

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.");
    }
}

ラムダ式を使用した Runnable の実装


呼び出し可能と未来

CallableJava で非同期操作を使用する必要がある場合は、と をよく使用しますFuture。これら 2 つのインターフェイスの関連する実装を詳しく見てみましょう。

  • 呼び出し可能
/*
 * 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定義されて使用され始めたばかりであることがわかります。インターフェースは 1 つだけ定義されています。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();
}

RunnableFuture2 つのインターフェースの組み合わせRunnableFuture

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()メソッドは依然として実装のために呼び出されます。

その他

時間の都合上、ここでは run メソッドのみを紹介しますが、後ほど他のメソッドcancel()get()メソッドもいくつか追加していきます。

おすすめ

転載: blog.csdn.net/u010416101/article/details/121600269