Javaマルチスレッドプログラミング技術における高度な応用


スレッドグループスレッドは個別に作成されますが、デバッグおよび監視のためにスレッドグループに分類できます.スレッドを作成しながら1つのスレッドグループにのみ関連付けることができます.大量のスレッドを使用するプログラムでは、スレッドグループを使用してスレッドを整理することが役立ちます.コンピュータ上のディレクトリとファイル構造と見なすことができます.スレッド間送信スレッドが実行を継続する前に条件を待つ必要がある場合、synchronizedキーワードだけでは不十分です.synchronizedキーワードは、オブジェクトの同時更新を阻止しますが、スレッド間送信は実現されません.Objectクラスにはwait()、notify()、notifyAll()の3つの関数があります.世界気候予測プログラムを例に挙げる.これらのプログラムは地球を多くのユニットに分けることによって、各サイクルでは、これらの値が安定になるまで、各ユニットの計算は隔離され、隣接するユニット間でいくつかのデータが交換されます.したがって、本質的には、各ループで各スレッドがすべてのスレッドがそれぞれのタスクを完了してから次のループに入るのを待たなければならない.このモデルをシールド同期と呼び、次の例ではシールド同期
<ccid_code>public class BSync {
 int totalThreads;
 int currentThreads;

 public BSync(int x) {
  totalThreads = x;
  currentThreads = 0;
 }

 public synchronized void waitForAll() {
  currentThreads++;
  if(currentThreads < totalThreads) {
   try {
    wait();
   } catch (Exception e) {}
  }
  else {
   currentThreads = 0;
   notifyAll();
  }
 }
}</ccid_code>

1つのスレッドに対してwait()を呼び出すと、そのスレッドは有効にブロックされ、別のスレッドが同じオブジェクトに対してnotify()またはnotifyAll()を呼び出すまでのみブロックされます.したがって、前の例では、異なるスレッドは、それらの作業が完了するとwaitForAll()関数を呼び出し、最後のスレッドはnotifyAll()関数をトリガーし、この関数はすべてのスレッドを解放します.3番目の関数notify()は、待機中のスレッドを1つだけ通知します.この関数は、1つのスレッドでしか使用できないリソースにアクセス制限がある場合に便利です.ただし、Java仮想マシン(JVM)スケジューリングアルゴリズムに依存するため、どのスレッドがこの通知を取得するかを予知することはできません.別のスレッドにCPUを譲ります.スレッドがデータベース接続やネットワークポートなどの希少なリソースを放棄した場合、yield()関数を呼び出して一時的に優先度を下げ、他のスレッドが実行できるようにします.デーモン・スレッドには、ユーザー・スレッドとデーモン・スレッドの2種類があります.ユーザー・スレッドは、有用な作業を完了するスレッドです.デーモンスレッドは、アシスト機能のみを提供するスレッドです.ThreadクラスはsetDaemon()関数を提供します.Javaプログラムは、すべてのユーザースレッドが終了するまで実行され、すべてのデーモンスレッドが破壊されます.Java仮想マシン(JVM)では、mainが終了しても、別のユーザースレッドがまだ実行されている場合でも、プログラムは実行を続行できます.使用を提唱しない方法を避ける使用を提唱しない方法は、後方互換性をサポートするために保持されている方法であり、以降のバージョンでは現れる可能性があり、現れない可能性があります.Javaマルチスレッドサポートはバージョン1.1とバージョン1.2で重大な改訂が行われており、stop()、suspend()、resume()関数は使用されていません.これらの関数はJVMに微妙なエラーを導入する可能性があります.関数名は魅力的に聞こえるかもしれませんが、誘惑に抵抗して使用しないでください.スレッド化されたプログラムをデバッグしてオンライン化するプログラムでは、デッドロック、ライブロック、メモリ破損、リソース消費が発生する可能性があります.デッドロックデッドロックは、マルチスレッドプログラムで最も一般的な問題かもしれません.1つのスレッドにリソースが必要で、別のスレッドがリソースのロックを持っている場合、デッドロックが発生します.このような状況は通常検出しにくい.しかし、ソリューションはかなり良いです.すべてのスレッドで同じ順序ですべてのリソースロックを取得します.たとえば、4つのリソースがある場合?A、B、C、D?また、1つのスレッドが4つのリソースのいずれかのリソースのロックを取得する場合は、Bに対するロックを取得する前にまずAに対するロックを取得することを確認してください.「スレッド1」がBおよびCに対するロックを取得し、「スレッド2」がA、CおよびDのロックを取得する場合、この技術はブロックを引き起こす可能性があるが、この4つのロックにデッドロックをもたらすことは決してない.ロックは、スレッドが新しいタスクを受け入れるのに忙しいため、タスクを完了する機会がない場合に発生します.このスレッドは最終的にバッファを超え、プログラムがクラッシュします.秘書が手紙を入力する必要があると思いますが、彼女はずっと電話に出るのに忙しいので、この手紙は永遠に入力されません.メモリ破損synchronizedキーワードを賢明に使用すれば、メモリエラーという腹立たしい問題を完全に回避できます.ファイル記述子などのシステムリソースを消費するリソースは限られています.マルチスレッド・プログラムは、各スレッドがこのようなリソースを望んでいる可能性があるため、リソースを消費する可能性があります.スレッド数がかなり大きい場合、またはリソースの候補スレッド数が使用可能なリソース数をはるかに上回っている場合は、リソースプールを使用することが望ましい.最良の例は、データベース接続プールです.スレッドがデータベース接続を使用する必要がある場合は、プールから1つを取り出し、使用してからプールに戻します.リソースプールはリポジトリとも呼ばれます.大量のスレッドをデバッグすると、1つのプログラムが大量のスレッドが実行されているため、デバッグが極めて困難になることがあります.この場合、以下のクラスが役に立つ可能性があります:
<ccid_code>public class Probe extends Thread {
 public Probe() {}
 public void run() {
  while(true) {
   Thread[] x = new Thread[100];
   Thread.enumerate(x);
   for(int i=0; i<100; i++) {
    Thread t = x[i];
    if(t == null)
     break;
    else
     System.out.println(
t.getName() + "\t" + t.getPriority()+ "\t" + t.isAlive() + "\t" +
t.isDaemon());
   }
  }
 }
}</ccid_code>

スレッド優先度の制限とJavaスレッドモデルのスケジューリングは、動的に変更できるスレッド優先度に関連します.本質的に、スレッドの優先度は1から10の間の数値であり、数値が大きいほどタスクが緊急であることを示します.JVM規格では、優先度の高いスレッドが最初に呼び出されてから、優先度の低いスレッドが呼び出されます.ただし、この基準は、同じ優先度を有するスレッドに対する処理はランダムである.これらのスレッドをどのように処理するかは、末端のオペレーティングシステムポリシーに依存します.場合によっては、優先度が同じスレッドが時間分割で実行されます.別の場合、スレッドは終了まで実行されます.Javaは10の優先度をサポートしており、末端オペレーティングシステムのサポートの優先度はずっと少ない可能性があります.これは混乱を引き起こす可能性があります.したがって、優先度は大まかなツールとしてしか使用できません.最後の制御はyield()関数を賢明に用いることによって達成できる.通常、スレッドの優先度に依存してスレッドの状態を制御しないでください.ここでは、Javaプログラムでスレッドを使用する方法について説明します.スレッドを使用すべきかどうかというより重要な問題は、手元のアプリケーションによって大きく異なります.アプリケーションでマルチスレッドを使用するかどうかを決定する方法の1つは、並列に実行できるコード量を推定することです.マルチスレッドを使用してもCPUの能力は増加しないことを覚えておいてください.ただしJVMのローカルスレッドで実装すると、異なるスレッドは、異なるプロセッサ上で同時に動作することができ(マルチCPUの機器で)、マルチCPU機器を十分に利用することができる.アプリケーションが計算密集型であり、CPU機能の制約を受ける場合、マルチCPU機器だけがより多くのスレッドから利益を得ることができる.アプリケーションが遅いリソースを待たなければならない場合(ネットワーク接続やデータベース接続など)の場合、またはアプリケーションが非インタラクティブである場合、マルチスレッドは通常有利です.インターネットベースのソフトウェアはマルチスレッドである必要があります.そうでない場合、ユーザーはアプリケーションの反応が鈍いと感じます.たとえば、多数のクライアントをサポートするサーバを開発する場合、マルチスレッドはプログラミングを容易にすることができます.この場合、このスレッドは、異なる顧客または顧客グループにサービスを提供し、応答時間を短縮します.一部のプログラマーは、Cや他の言語でスレッドを使用したことがあります.それらの言語ではスレッドに言語サポートがありません.これらのプログラマーは通常、スレッドに自信を失っている可能性があります.