[자바 동시 프로그래밍] 자바 멀티 스레딩 (2 개) : Callable & Future & FutureTask 소스 코드 분석

1. 스레드 작업 구성표 1 생성 : 실행 가능

runnable에는 반환 값이 없으며 run은 스레드 논리를 구현합니다.

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

2. 스레드 태스크 프로그램 2 생성 : 호출 가능

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 parameter 메소드를 더 많이 사용하고 get parameterless 메소드를 덜 사용하여 작업 실행이 너무 느리고 대부분의 스레드가 대기하여 스레드 고갈 문제를 일으키는 것을 방지하는 것이 좋습니다.

  • 취소 방법은 주로 작업을 취소하는 데 사용됩니다. 작업이 실행되지 않은 경우 취소 할 수 있습니다. 작업이 이미 실행중인 경우 취소하지 않도록 선택하거나 실행중인 작업을 직접 중단 할 수 있습니다.

그렇다면 여기에 문제가 있습니다 Callable과 Future는 모두 인터페이스입니다. Future를 통해 Callable을 제어하는 ​​방법은 무엇입니까? 중간 클래스를 만들어 Future 인터페이스를 구현 한 다음 Callable을 결합하고 마지막으로 Future 인터페이스의 메서드를 통해 제어 할 수 있습니다. 여기의 특정 논리는 FutureTask에서 볼 수 있습니다.

3. FutureTask

Runnable과 Callable은 모두 스레드가 수행 할 작업을 나타낼 수 있습니다. 그러면이 두 인터페이스가 서로 관련되어서는 안된다는 기준으로 어떻게 서로 변환 할 수 있습니까?

  • 우선, Runnable과 Callable은 extends에 의해 구현되지 않아야합니다. 직접 상속은 is-a 관계이고이 두 가지는 분명히 그렇지 않기 때문입니다.
  • 그런 다음 중급 클래스 F를 도입하는 또 다른 방법이 있습니다 (다음 두 가지 조건을 충족).
    • 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 : Future의 Callable 제어 실현
public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    
    void run();
}

FutureTask가 Future 인터페이스와 Runnable 인터페이스에서 메소드를 구현하는 방법을 살펴 보겠습니다.

3.2 미래 인터페이스 방법 구현

  • Future의 실현을 통해 Callable의 결합은 두 가지의 결합을 실현합니다.
    • 생성자는 Callable에서 전달
    • 결과 연산을 반환하는 Future 메서드 구현
  • FutureTask는 Runnable을 구현하고 생성자가 Callable에 전달되면 Runnable로 직접 변환됩니다.

가져 오기

get에는 무한 차단과 타임 아웃의 두 가지 방법이 있습니다. 일반적으로 타임 아웃이있는 방법을 권장합니다. 소스 코드는 다음과 같습니다.

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 메소드는 반환 값이 없으며, 결과 속성 (set (result))에 값을 할당하면 get시 결과 속성에서 반환 값을 얻을 수 있습니다.
  • FutureTask의 두 생성자는 최종적으로 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 호출 가능으로 실행

  • 생성자는 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. 매개 변수가있는 작업과없는 작업의 두 가지 정의 방법이 통합되어 사용하기 편리합니다.

추천

출처blog.csdn.net/weixin_43935927/article/details/108615345