javaの同時非同期プログラミング・インターフェース10元今だけ取得するには一つのインタフェースを必要とするライブ!

什么?对你没有听错,也没有看错 ..多线程并发执行任务,取结果归集~~ 不再忧愁....感谢大家的双击+点赞和关注 下面起锅烧油


入門

まずN以上の間、その上のページをデータのAPP取得のいくつかを見て、そして、例えば、約10までのユーザーの行動データ、:親指の数、掲載された論文の数、このようなポイント数、メッセージの数、数の問題、グッズ数、ファンの数、カード番号のクーポン、赤の数が...........〜私たちはこれらの数字を見て本当に多くのです。

通常、Nマルチインタフェース上のデータ・インタフェースへのインタフェース(あなたが一緒に書くとき、半分分まで対応すると推定されている10個のクエリので)、ページを取得するには、赤ちゃんのフロントエンドは本当に疲れ、前面開口部のマルチスレッドですレイアは、私たちは赤ちゃんの体重のフロントエンドの後端を見てみたい、すべての後に、と言って「があるプログラマプログラマ-物事を困難にする理由。」

今日は、データ泥棒TM速いリターンをインタフェースすることができます - また、障害物が発生し、プログラミング、苦痛シリアルプログラミングに取り組む〜

マルチスレッドのタスクは、帰属の結果を取ります

今日の豚は今後、FutureTask、ExecutorServiceのです...

  • すべての年齢の結果を得るために、FutureTaskタスクを過ごし、CPUの消費量があります。FutureTaskロックはまた、(抽象計算結果を表し、未来セマンティック実装)行うことができます。呼び出し可能なことで属性として(同等の結果のRunnableを生成することができます)、その後、Runnableを、継承するアクチュエータとしてそれを自分自身を置くFutureTaskを、実際に行動をキャンセルされた支持非同期タスクの実行です
  • 。呼び出し可能はRunnableをするコールバックインタフェース、あなたは一般的な戻り値の型を宣言することができ、方法およびスレッドが実行される。これは非常に単純である - それは本当に非常に簡単ですので、我々は良い見て、ソースコードの深い理解に行きたい~~
  • FutureTaskはキャンセル、開始、クエリや他の機能を提供し、かつ、Runnableインタフェースを実装して、あなたがスレッドに送信することができ、未来を実現しました。
  • Java並行処理ツール3つのトリック 状态,队列,CAS

状態

 /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:        //可能发生的状态过度过程
     * NEW -> COMPLETING -> NORMAL        // 创建-->完成-->正常
     * NEW -> COMPLETING -> EXCEPTIONAL   // 创建-->完成-->异常
     * NEW -> CANCELLED                   // 创建-->取消
     * NEW -> INTERRUPTING -> INTERRUPTED // 创建-->中断中-->中断结束
     */
    
    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;    // 中断结束      任务被中断

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
复制代码

図はそれを理解していません。

  • 未来

      (1)cancle    可以停止任务的执行 但不一定成功 看返回值true or  false
      (2)get       阻塞获取callable的任务结果,即get阻塞住调用线程,直至计算完成返回结果
      (3)isCancle  是否取消成功
      (4)isDone    是否完成
    复制代码

ハイライト:

Furture.get()は、タスク状態に完了するまでの結果は、すぐにそうでなければブロックを返され、タスクが完了している場合、結果の値は、実行の状態に依存して取得し、その結果を返すか、例外をスローします。

すべての状態が終わる可能性があり、異常終了し、終了に伴う解約し、通常の終了など、その計算を示し、「ファイル名を指定して実行が完了しました」。完了状態に入ると、彼は、この状態で停止します限り、状態はでていないようNEWな状態は、タスクが完了したことを意味します。

ロックフリーのプログラミングCPUの消費量を維持するために - このスレッドのスレッドを呼び出すためのタスクを実行するためのスレッドからの演算結果に責任FutureTask、およびセキュリティスレッドを確保するために、転送セキュリティ解除処理結果UNSAFEロックフリーのプログラミング技術を確保しますしたがって、状態フラグ、CPUのアイドル時間の圧力を減少させること

  • タスクの神:呼び出し可能
  • エグゼクティブタスク:ランナー
  • タスクの結果:結果
  • 状態+成果+ウェイター:タスクの結果を取得します。
  • 中断またはキャンセルされたタスク:状態+ランナー+ウェイター

runメソッド

  1. 、状態を確認してください非NEW、それが始まったことを説明し、直接リターン、そうでない場合は、現在のスレッドのランナーを設定し、成功は、そうでない場合は、返品を継続していきます。
  2. タスクを実行するためにCallable.call()メソッドを呼び出し、コールセット(結果)の成功は方法がsetException(EX)メソッドを呼び出すために失敗し、最終的に状態を設定し、finishCompletion()メソッドを呼び出しますが、ブロックされたスレッドがget()メソッドに目覚めていますそれら。
  3. 示さ注意として、変数が省略された場合には、走って、「設定(結果);」tryブロック内のステートメントは、「=真走った。」何が起こるかで文を?ビューの最初に、コードのロジック・ポイント、問題はない、しかし、考慮に入れる「セット(結果);」の方法は、それが間違っている場合であっても例外をスロー?set()メソッド)は、ユーザ定義の方法、従って、省略することができない(最終的に行うために呼び出します。
  4. 状態が中断されている場合は、CPUへの取り組み、スピン待ちの割り込み処理を実行する他のスレッド。handlePossibleCancellationInterrupt(int型秒)メソッドを参照してください。
public void run() {
        // UNSAFE.compareAndSwapObject, CAS保证Callable任务只被执行一次 无锁编程
        if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = 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); // 记录异常
                }
                if (ran) // 问题:ran变量可以省略吗,把set(result);移到try块里面?
                    set(result); // 设置结果
            }
        } finally {
            runner = null; // 直到set状态前,runner一直都是非空的,为了防止并发调用run()方法。
            int s = state;
            if (s >= INTERRUPTING) // 有别的线程要中断当前线程,把CPU让出去,自旋等一下
                handlePossibleCancellationInterrupt(s);
        }
    }
复制代码
      private void handlePossibleCancellationInterrupt(int s) {
         if (s == INTERRUPTING) // 当state为INTERRUPTING时
             while (state == INTERRUPTING) // 表示有线程正在中断当前线程
                 Thread.yield(); // 让出CPU,自旋等待中断
     }
复制代码

ロングったらしいの下では、次のことを行うために、キーユニットを実行します。

将runner属性设置成当前正在执行run方法的线程
调用callable成员变量的call方法来执行任务
设置执行结果outcome, 如果执行成功, 则outcome保存的就是执行结果;如果执行过程中发生了异常, 则outcome中保存的就是异常,设置结果之前,先将state状态设为中间态
对outcome的赋值完成后,设置state状态为终止态(NORMAL或者EXCEPTIONAL)
唤醒Treiber栈中所有等待的线程
善后清理(waiters, callable,runner设为null)
检查是否有遗漏的中断,如果有,等待中断状态完成。
复制代码

どのように少ないことができますそれを行うための方法は、取得を参照してください。ブロックされています:awaitDone

    public V get() throws InterruptedException, ExecutionException {
        int s = state; // 执行器状态
         if (s <= COMPLETING) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待
             s = awaitDone(false, 0L); // 等待
         return report(s); // 报告结果
     }
复制代码

ちなみに密かに見て(ロング、TimeUnitで)を取得し、タイムアウト後にタイムアウトを拡張し、向上させる方法を取得することで、私は怒っていなかった例外....

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) // 如果状态小于等于COMPLETING,说明任务正在执行,需要等待;等待指定时间,state依然小于等于COMPLETING
            throw new TimeoutException(); // 抛出超时异常
        return report(s); // 报告结果
    }
复制代码

だから、見てawaitDoneを使用すると、無限ループを書く知って、while(true)|for (;;)マスターであります-

private int awaitDone(boolean timed, long nanos) throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L; // 计算deadline
        WaitNode q = null; // 等待结点
        boolean queued = false; // 是否已经入队
        for (;;) {
            if (Thread.interrupted()) { // 如果当前线程已经标记中断,则直接移除此结点,并抛出中断异常
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state; // 执行器状态
            if (s > COMPLETING) { // 如果状态大于COMPLETING,说明任务已经完成,或者已经取消,直接返回
                if (q != null)
                    q.thread = null; // 复位线程属性
                return s; // 返回
            } else if (s == COMPLETING) // 如果状态等于COMPLETING,说明正在整理结果,自旋等待一会儿
                Thread.yield();
            else if (q == null) // 初始,构建结点
                q = new WaitNode();
            else if (!queued) // 还没入队,则CAS入队
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            else if (timed) { // 是否允许超时
                nanos = deadline - System.nanoTime(); // 计算等待时间
                if (nanos <= 0L) { // 超时
                    removeWaiter(q); // 移除结点
                    return state; // 返回结果
                }
                LockSupport.parkNanos(this, nanos); // 线程阻塞指定时间
            } else
                LockSupport.park(this); // 阻塞线程
        }
    }
复制代码

この時点で、スレッドのスケジューリングタスク、私は探検するために長いったらしい~~~~まだたくさん得ることはありません、すべての後に、チャット支払わタイト、私は詳細には触れません-

キュー

その後、我々は待ち行列を見て、FutureTaskでは、キューは、それが実行スレッドを完了したすべての保留中のタスクのセットを表し、単独リンクリストです。私たちは、FutureTaskはあなたが結果を取得する場合、タスクはそれを行う方法を完了していない、あなたは、結果の「タスク」を得ることができます未来のインターフェースを実現知っていますか?その後、スレッドは、タスクが起こされるために終了するまで待って保留キューで結果を取得します。これは、同期キューAQS私たちの前回の学習に似ていて、以下の分析では、我々は彼らの類似点と自分自身の違いを制御することができます。

我々が言ったように、並行プログラミングのキューの使用は通常、待ちキューに投入されたデータ構造の特定の種類の中にパッケージ現在のスレッドで、私たちは各ノードのキューを見て、どのような構造です。

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}
复制代码

目に見える、AQSさに比べsync queueに使わ二重リンクリストNode、これはWaitNode簡単で、それは次のスレッドの属性が含まれており、ノードのレコードスレッドにポイントを属性。

FutureTaskは、このようにリンクリストをTreiberとして使用する正確なスタックするスタックとして使用され、TreiberスタックはGeshaである、ことを言及する価値があることを理解していないだけで、スレッドセーフなスタックとして考えることができそれは(あなたがこの記事を参照することができ、より多くを学びたい)CASプッシュとポップ操作を使用して行われます。なぜ我々は同じ時間は、タスクの結果を得るには、複数のスレッドが存在する可能性があるので、タスクが実装の過程で残っている場合、これらのスレッドはTreiberスタックスタックをスローWaitNodeにパッケージ化され、スレッドセーフなスタックを使用しますトップ、完全なスタック操作、同時にCASはスタックにプッシュ保証こうしてスレッドセーフ操作の使用を必要とする、起こり得るプッシュ複数のスレッドの場合があるので、スタックはまた、同様の理由の場合です。

キューは本質的FutureTaskあるためTreiber(ドライブ)スタック、キューのみライン上のノードを積層するポインタを使用する必要があります、FutureTaskにウェイターの特性です。

/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
复制代码

実際には、リンクリストの最初のノード全体の方法です。

要約すると、として使用されるキュー構造FutureTaskは次のとおりです。

CAS操作

ほとんどのCAS操作はFutureTask例外なく、状態を変更するために使用されます。オフセット性CAS操作を初期化するために必要なコードの私たちは、一般的に、静的なブロック:

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }
复制代码

この静的コードブロックから、我々はまた、状態、ランナーやウェイターなど、三つの主要な属性、のためのCAS操作が同時に複数のスレッドによってアクセスされる三つの基本的な属性を示して、見ることができます。タスクの状態を表す状態プロパティ、ウェイタープロパティは、トップへのノードポインタのポイントを表し、私たちは前に上記の二つを分析しました。ランナープロパティは、「タスク」であるFutureTaskスレッドの実行を表します。なぜ我々は、タスクを実行するスレッドを記録するために、プロパティが必要なのでしょうか?これは、誰がそれを中断たちができるタスクを実行するだけのスレッドを知って、準備するタスクを中断または中止することです。

完成した定義の属性は、次の操作自体CASで、オフセット。FutureTaskでは、CASの動作や、最終的な呼び出しUnsafeクラスcompareAndSwapXXXメソッドは、安全でない上、以来支払わコードテキストはここでは繰り返しません。

実践的な演習

一切没有例子的讲解都是耍流氓 >>> 起锅烧油


私はこの問題をgitの - 私はのための実際のコードを作るために夜に家に帰ります

おすすめ

転載: juejin.im/post/5d3c46d2f265da1b9163dbce