[Javaコンカレントプログラミング] Javaマルチスレッド(2):Callable&Future&FutureTaskソースコード分析

1.スレッドタスクスキーム1を作成:Runnable

runnableには戻り値がありません。runはスレッドロジックを実装します

public interface Runnable {
    
    
    public abstract void run();
}

2.スレッドタスクプログラム2を作成:Callable

2.1呼び出し可能

callableは戻り値(V)を持ち、呼び出しはスレッドロジックを実装します

public interface Callable<V> {
    
    
    V call() throws Exception;
}

2.2将来

Callabeは単独で使用することはできません。Callableの実行を制御し、Callableの実行結果を取得するにはfutureが必要です。

public interface Future<V> {
    
    
    // 如果任务已经成功了,或已经取消了,是无法再取消的,会直接返回取消成功(true)
    // 如果任务还没有开始进行时,发起取消,是可以取消成功的。
    // 如果取消时,任务已经在运行了,mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程
    // mayInterruptIfRunning 为 false,表示不能打断直接返回
    boolean cancel(boolean mayInterruptIfRunning);
	
    // 返回线程是否已经被取消了,true 表示已经被取消了
    // 如果线程已经运行结束了,isCancelled 和 isDone 返回的都是 true
    boolean isCancelled();

    // 线程是否已经运行结束了
    boolean isDone();

    // 等待结果返回
    // 如果任务被取消了,抛 CancellationException 异常
    // 如果等待过程中被打断了,抛 InterruptedException 异常
    V get() throws InterruptedException, ExecutionException;

    // 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}
  • getメソッドの主な機能は、Callable非同期タスクの実行結果を取得することです。パラメータなしのgetは、タスクの実行が完了するのを待ってから戻ります。パラメータ付きのgetメソッドは、固定時間を設定できます。タスクが正常に実行されなかった場合、設定時間内、例外を直接返します。実際の作業では、タスクの実行が遅くなりすぎてほとんどのスレッドが待機し、スレッドの枯渇の問題が発生するのを防ぐために、パラメーターの取得メソッドを増やし、パラメーターのないメソッドを減らすことをお勧めします。

  • cancelメソッドは主にタスクをキャンセルするために使用されます。タスクが実行されていない場合はキャンセルできます。タスクが既に実行中の場合は、キャンセルしないか、実行中のタスクを直接中断するかを選択できます。

次に、ここに問題があります。CallableとFutureはどちらもインターフェースです。Futureを介してCallableを制御する方法は?中間クラスを作成してFutureインターフェースを実装し、次にCallableを組み合わせて、最後にFutureインターフェースのメソッドを通じて制御を実現できます。ここでの特定のロジックは、FutureTaskで確認できます。

3.FutureTask

RunnableとCallableの両方がスレッドによって実行されるタスクを表すことができるので、これらの2つのインターフェースを、それらが関連しないはずであることに基づいて、どのように相互に変換できますか?

  • まず最初に、直接継承はis-a関係であり、これら2つは明らかにそうではないので、RunnableとCallableはextendsで実装してはなりません。
  • 次に、中間クラスFを導入する別の方法があります(次の2つの条件を満たす)
    • FはRunnableを拡張します:继承Runnable
    • F {一次呼び出し可能c}:组合呼び出し可能

==>最後に、上記のすべての分析を組み合わせて、FutureTaskの構造を取得します。Futureインターフェースの実装とRunnableインターフェースの実装とCallableの組み合わせ

public class FutureTask<V> implements RunnableFuture<V> {
    
    
	// 任务状态
    private volatile int state;
    private static final int NEW          = 0;//线程任务创建
    private static final int COMPLETING   = 1;//任务执行中
    private static final int NORMAL       = 2;//任务执行结束
    private static final int EXCEPTIONAL  = 3;//任务异常
    private static final int CANCELLED    = 4;//任务取消成功
    private static final int INTERRUPTING = 5;//任务正在被打断中
    private static final int INTERRUPTED  = 6;//任务被打断成功

    // 组合了 Callable 
    private Callable<V> callable;
    
    // 异步线程返回的结果
    private Object outcome; 
    // 当前任务所运行的线程
    private volatile Thread runner;
    // 记录调用 get 方法时被等待的线程
    private volatile WaitNode waiters;
    
    //---------------------------构造方法---------------------------------
    // 构造器中传入Callable接口实现对象,对callable成员变量进行初始化
    public FutureTask(Callable<V> callable) {
    
    
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // 任务状态初始化
        this.state = NEW;       // ensure visibility of callable
    }

    // 使用 Runnable 初始化,并传入 result 作为返回结果。
    // Runnable 是没有返回值的,所以 result 一般没有用,置为 null 就好了
    public FutureTask(Runnable runnable, V result) {
    
    
        // Executors.callable 方法把 runnable 适配成 RunnableAdapter,
        // RunnableAdapter 实现了callable,所以也就是把 runnable 直接适配成了 callable。
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
}

3.1 RunnableFuture

最初に、FutureTaskによって実装されるRunnableFutureインターフェースを確認します。

  • Runableを継承:外部をRunnableに変換
  • Inherit Future:CallableのFutureの制御を実現する
public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    
    void run();
}

FutureTaskがFutureインターフェースとRunnableインターフェースのメソッドを実装する方法を見てみましょう。

3.2 Futureインターフェースメソッドの実装

  • 未来の実現を通じて、Callableの組み合わせは、2つの組み合わせを実現します
    • Callableでコンストラクターが渡される
    • 結果の操作を返すFutureメソッドを実装する
  • FutureTaskはRunnableを実装し、コンストラクターがCallableに渡されると、直接Runnableに変換されます

取得する

getには、無限ブロッキングとタイムアウト付きの2つのメソッドがあります。通常、タイムアウト付きのメソッドをお勧めします。ソースコードは次のとおりです。

public V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException {
    
    
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    // 如果任务已经在执行中了,并且等待一定的时间后,仍然在执行中,直接抛出异常
    if (s <= COMPLETING &&
        (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    // 任务执行成功,返回执行的结果
    return report(s);
}

// 等待任务执行完成
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    
    
    // 计算等待终止时间,如果一直等待的话,终止时间为 0
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    // 不排队
    boolean queued = false;
    // 无限循环
    for (;;) {
    
    
        // 如果线程已经被打断了,删除,抛异常
        if (Thread.interrupted()) {
    
    
            removeWaiter(q);
            throw new InterruptedException();
        }
        // 当前任务状态
        int s = state;
        // 当前任务已经执行完了,返回
        if (s > COMPLETING) {
    
    
            // 当前任务的线程置空
            if (q != null)
                q.thread = null;
            return s;
        }
        // 如果正在执行,当前线程让出 cpu,重新竞争,防止 cpu 飙高
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        // 如果第一次运行,新建 waitNode,当前线程就是 waitNode 的属性
        else if (q == null)
            q = new WaitNode();
        // 默认第一次都会执行这里,执行成功之后,queued 就为 true,就不会再执行了
        // 把当前 waitNode 当做 waiters 链表的第一个
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        // 如果设置了超时时间,并过了超时时间的话,从 waiters 链表中删除当前 wait
        else if (timed) {
    
    
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
    
    
                removeWaiter(q);
                return state;
            }
            // 没有过超时时间,线程进入 TIMED_WAITING 状态
            LockSupport.parkNanos(this, nanos);
        }
        // 没有设置超时时间,进入 WAITING 状态
        else
            LockSupport.park(this);
    }
}

キャンセル

タスクをキャンセルし、実行中の場合は中断してください

public boolean cancel(boolean mayInterruptIfRunning) {
    
    
    if (!(state == NEW &&//任务状态不是创建 并且不能把 new 状态置为取消,直接返回 false
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    // 进行取消操作,打断可能会抛出异常,选择 try finally 的结构
    try {
    
        // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
    
    
            try {
    
    
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally {
    
     // final state
                //状态设置成已打断
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
    
    
        // 清理线程
        finishCompletion();
    }
    return true;
}

3.3 Runnableインターフェースメソッドの実装

走る

runメソッドを直接呼び出すか、新しいスレッドを開いて呼び出すことができます

  • runメソッドには戻り値がありません。outcomeプロパティ(set(result))に値を割り当てることにより、getのときに結果プロパティから戻り値を取得できます。
  • FutureTaskの2つのコンストラクターは最終的にCallableに変換されるため、runメソッドが実行されると、Callableのcallメソッドのみを実行する必要があります。c.call()コードが実行されると、入力パラメーターがRunnableの場合、呼び出しパスc.call()-> RunnableAdapter.call()-> Runnable.run()です。入力パラメーターがCallableの場合は、直接呼び出します。
public void run() {
    
    
    // 状态不是任务创建,或者当前任务已经有线程在执行了,直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
    
    
        Callable<V> c = callable;
        // 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);
            }
            // 给 outcome 赋值
            if (ran)
                set(result);
        }
    } finally {
    
    
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

3.4 callnaへのrunnale

  • コンストラクターはRunnableと戻り値を渡し、変換後にcallableに割り当てます
public FutureTask(Runnable runnable, V result){
    
    
    // 将转化的Callable赋值给callable
    this.callable = Executors.callable(runnable, result);
}
  • アダプターモード:Executors.callable()-> RunnableAdpter
// 转化 Runnable 成 Callable 的工具类
// 自己实现Callable接口
static final class RunnableAdapter<T> implements Callable<T> {
    
    
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
    
    
        this.task = task;
        this.result = result;
    }
    // 重写call方法,返回result
    public T call() {
    
    
        task.run();
        return result;
    }
}

最後に、FutureTaskの機能を要約します。

  1. Futureのすべてのメソッドを実現し、タスクの特定の管理機能(タスクの実行結果の取得、タスクのキャンセル、タスクの中断など)を備えています。
  2. Callableを組み合わせ、Runnableを実装し、CallableとRunnnableを直列に接続します。
  3. パラメータのあるタスクとないパラメータの2つの定義方法は統一されており、使いやすいです。

おすすめ

転載: blog.csdn.net/weixin_43935927/article/details/108615345
おすすめ