Javaスレッドプールの多重化理解


更新2019-01-15
後で考えてみると、一番大切なものは言わなかったと思います.次の内容は自分でしばらく隔てて理解していません.主に今言いたい最も重要なものは、スレッドのstartとrunです.startは新しいスレッドを起動し、runはスレッドを実行する方法です.多重化は複数の異なるrunを1つのスレッドのrun内に書き、startというスレッドです.事前に書き込む場合、実際には多重化ではなく、スレッドプールは動的多重化を実現する.すなわち、スレッド実行中に、動的にブロックキューからタスクを取り出し、スレッドrunメソッドに追加して多重化を実現する.一番大切なのはこれでしょう.
前情
今日は私が3回目にスレッドプールのソースコードを見て、やっとこの少しの理解を書くことができて、私のブログは普通はすべて自分で勉強したノートと心得ですが、結局インターネットの上で、需要のある同業者は見て、やはり人を害することはできません.3回目とはいえ、細部の条件分岐はまだよく理解されていないが、著者の考え方をマクロ的に少し理解しただけだ.以下に説明します.
プール化コア
私の理解では、プール化技術は、接続プールでもスレッドプールでも、コアは多重二字である.プール化技術は簡単に言えば、比較的「高価」な資源を再利用することである.Java内のスレッドプールには一連の実装継承関係があり、最上位層への実装クラスはThreadPoolExecutorクラスである.このスレッドプールがどのように多重化を実現するかは、私が最も関心を持っているが、見てみると、実は簡単だ.このクラスの多重化は1つの内部クラスWorkerを用いることであり、まず、この内部クラスはRunnableを実現し、AbstractQueuedSynchronizerクラスを継承している.後者については、Workerの内部独占ロックメカニズムを担当していることを一時的に知る必要がある.私たちが注意しているのはやはりRunnableであり、私たちは正常に1つのスレッドを作成するいくつかの方法を知っている.Runnableを実現することはその1つの方法である.Runnableを実現するのはおかしくないが、実現と同時に、Worker内部にはThreadタイプの属性がある.この2つのことは単独で見ると、すべて正常ですが、一緒に置くと、私はとても理解できません.それからコンストラクタを見て、ここにソースコードを貼ってください.
 Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

パラメータはよく知っていて、私たちがRunnableを実現するクラスを使ってスレッドを作成する道理と同じように、他にかかわらず、コアは最後の文、this.thread = getThreadFactory().newThread(this);という意味で、getThreadFactory().newThread()は私たちが使用しているnew Thread()と見なすことができて、パラメータがthisであることを見て、つまりWorkerで1つのスレッドを作成して、それからWoker内部のThreadを指します.
ここまで来て、私の学習時の疑問を言いたいのですが、なぜこのように書くのか、納得できませんでした.私は方向を変えました.もしそう書かなければ、多重化を実現する方法はありません.他の方法とソースコードの方法が対立しています.それはthreadとrunnableを別々に書くことです.スレッドプールの観点から見ると、Worker類のようなものを作成する必要がありますが、Runnableインタフェースを実現していません.実装runnable実装はタスクとしてブロックキューに格納され、私のクラスWorkerクラスコンストラクタも同様であるが、thisをスレッドの作成パラメータとしてではなく、受信runnableを使用する.どうやって起動しますか?クラスを使用thread.start()ですか?これは、転送されたものしか実行できません.私のクラスWorkerの内部のrunnableタスクを指定しても、前のスレッドが実行済みかどうかをどのように知って、外部にデッドループを書いて、ループの内部で現在のタスクを継続的に実行して、実行が終わったらキューから待っているタスクを取得して、実行しますか?また問題がありましたが、このループはどこに書いてありますか?各クラスWorkerにはループが必要ですか?この時私は突然一つのことに気づいた.もし私がこのように書いたら、毎回の任務はどのように実行するか、それともthread.start()ですか?私の理解によると、start()はrunnable実装内のrunを直接実行する方法とは異なり、startはオペレーティングシステムのシステム呼び出しに関連し、新しいスレッドを起動し、私はこのように書くのは多重化を実現していない.このようにして、私はやっとソースコードの書き方の深い意味を深く理解しました.
ソースコードにこのように書くのは、一時的に多重化を実現する唯一の方法であり、ワーク全体を1つのタスクとして本オブジェクトのThreadオブジェクトに提出して実行する場合、ワークのrunメソッドで複数のタスクを実行するrunメソッドが多重化である.
こんなにでたらめを言って、みんなはWorkerがrunnableを実現することを無視したかもしれません.それではrun方法を書き直さなければなりません.ソースコードを見てください.public void run() { runWorker(this); }は1行です.runWorker(this)、これでは何も見えません.
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

実はこのコードは難しくありませんが、多くの変数はコンテキストを結合しなければなりません.だから、私は簡単にこのコードが何をしたのかを話します.まず、Workerオブジェクトのインスタンス化時に入ってきたfirstTaskを手に入れて、その名の通り、説明しないで、それから1つのサイクルに入って、内部を循環して、現在のtaskを実行します.runメソッド、すなわち真の実行タスクは、ループごとにロックされ、独占的であることに注意しなければならない.現在のタスクを実行した場合、getTask()を実行し、タスクキューからタスクを取得し、タスクがないまでこのスレッドを閉じる.
このブログはしばらくここまでで、私は全体のスレッドプールに対してソースコードの解析を行っていないで、私もその能力がなくて、ただ私自身が多重化に対する理解を言って、ある困惑した同業者を助けることができることを望みます.