序文
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.");
}
}
呼び出し可能と未来
Callable
Java で非同期操作を使用する必要がある場合は、と をよく使用します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 ではシングルスレッドでもスレッドプールでも基本的にインターフェイスしか認識しないためThread
、Runnable
使用する際には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
2 つのインターフェースの組み合わせRunnable
。Future
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()
メソッドもいくつか追加していきます。