Java 101:Javaスレッドについて、パート4:スレッドグループ、揮発性、およびスレッドローカル変数

今月のJavaの101は、スレッドグループ、ボラティリティー、スレッドローカル変数、タイマー、およびに着目したスレッドシリーズを完結ThreadDeathクラス。

Javaスレッドを理解する-シリーズ全体を読む

  • パート1:スレッドとランナブルの紹介
  • パート2:スレッドの同期
  • パート3:スレッドのスケジューリング、待機/通知、およびスレッドの中断
  • パート4:スレッドグループ、ボラティリティ、スレッドローカル変数、タイマー、スレッドの停止

スレッドグループ

ネットワークサーバープログラムでは、1つのスレッドがクライアントプログラムからの要求を待機して受け入れ、データベーストランザクションや複雑な計算などを実行します。スレッドは通常、リクエストを処理するための新しいスレッドを作成します。リクエストの量によっては、多くの異なるスレッドが同時に存在する可能性があり、スレッド管理が複雑になります。簡素化スレッド管理に、プログラムは自分でスレッドを整理スレッドグループ-java.lang.ThreadGroupそのグループに関連するスレッドのオブジェクトThread(およびThreadサブクラス)オブジェクトが。たとえば、プログラムで使用ThreadGroupして、すべての印刷スレッドを1つのグループにグループ化できます。

注:説明を簡単にするために、スレッドを編成するかのようにスレッドグループを参照します。実際には、スレッドグループは、スレッドに関連付けられたオブジェクトを編成Thread(およびThreadサブクラス化)します。

Javaでは、system他のスレッドグループに参加するために、すべてのスレッドとすべてのスレッドグループ(ルートスレッドグループを保存)が必要です。この配置により、階層的なスレッドグループ構造が作成されます。これは、次の図がアプリケーションのコンテキストで示しています。

図の構造の上部には、systemスレッドグループがあります。 JVMで作成されたsystemグループは、オブジェクトのファイナライズやその他のシステムタスクを処理するJVMスレッドを編成し、アプリケーションの階層スレッドグループ構造のルートスレッドグループとして機能します。そのすぐ下にsystemは、JVMによって作成されたmainスレッドグループがあります。これはsystem、のサブスレッドグループ(略してサブグループ)です。main少なくとも1つのスレッド(main()メソッド内のバイトコード命令を実行するJVMで作成されたメインスレッド)が含まれます。

mainグループの下には、subgroup 1およびsubgroup 2サブグループ、アプリケーションで作成されたサブグループ(図のアプリケーションが作成する)があります。さらに、subgroup 1グループ3つのアプリケーション作成のスレッド:thread 1thread 2、とthread 3。対照的に、subgroup 2アプリケーションで作成された1つのスレッドをグループ化しますmy thread

基本を理解したところで、スレッドグループの作成を始めましょう。

スレッドグループを作成し、スレッドをそれらのグループに関連付けます

ThreadGroupクラスのSDKドキュメントは、2つのコンストラクタを明らかに:ThreadGroup(String name)ThreadGroup(ThreadGroup parent, String name)。どちらのコンストラクターもスレッドグループを作成し、nameパラメーターで指定されているように名前を付けます。コンストラクターは、新しく作成されたスレッドグループの親として機能するスレッドグループの選択が異なります。を除く各スレッドグループにsystemは、親スレッドグループが必要です。の場合ThreadGroup(String name)、親はを呼び出すスレッドのスレッドグループですThreadGroup(String name)。例として、メインスレッドがを呼び出すThreadGroup(String name)場合、新しく作成されたスレッドグループは、メインスレッドのグループを親として持ちmainます—。の場合ThreadGroup(ThreadGroup parent, String name)、親はparent参照するグループです。次のコードは、これらのコンストラクターを使用してスレッドグループのペアを作成する方法を示しています。

public static void main (String [] args) { ThreadGroup tg1 = new ThreadGroup ("A"); ThreadGroup tg2 = new ThreadGroup (tg1, "B"); }

上記のコードでは、メインスレッドは2つのスレッドグループを作成します:AB。まず、メインスレッドはAを呼び出して作成しThreadGroup(String name)ます。 -tg1参照されるスレッドグループの親はmainmainがメインスレッドのスレッドグループであるためです。次に、メインスレッドは。Bを呼び出すことによって作成しThreadGroup(ThreadGroup parent, String name)ます。tg2-referencedスレッドグループの親があるAためtg1の参照を引数として渡すThreadGroup (tg1, "B")Aを関連付けtg1

ヒント:ThreadGroupオブジェクトの階層が不要になったら、その階層の最上位にあるオブジェクトへの参照を介してThreadGroupvoid destroy()メソッドを呼び出しThreadGroupます。トップThreadGroupオブジェクトとすべてのサブグループオブジェクトにスレッドオブジェクトがない場合は、destroy()それらのスレッドグループオブジェクトをガベージコレクション用に準備します。それ以外の場合destroy()は、IllegalThreadStateExceptionオブジェクトをスローします。ただし、最上位ThreadGroupオブジェクトへの参照を無効にするまで(フィールド変数にその参照が含まれていると想定)、ガベージコレクターはそのオブジェクトを収集できません。前のコールがに行われた場合、トップオブジェクトの参照、あなたが決めることができますdestroy()呼び出すことによって方法ThreadGroupboolean isDestroyed()方法を。スレッドグループ階層が破棄された場合、このメソッドはtrueを返します。

それ自体では、スレッドグループは役に立ちません。使用するには、スレッドをグループ化する必要があります。ThreadGroup適切なThreadコンストラクターへの参照を渡すことにより、スレッドをスレッドグループにグループ化します。

ThreadGroup tg = new ThreadGroup ("subgroup 2"); Thread t = new Thread (tg, "my thread");

上記のコードは、最初に親グループとしてsubgroup 2グループを作成しますmain。(メインスレッドがコードを実行すると仮定します。)次に、コードmy threadThreadsubgroup 2グループ内にオブジェクトを作成します。

それでは、Figureの階層的なスレッドグループ構造を生成するアプリケーションを作成しましょう。

リスト1.ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("subgroup 1"); Thread t1 = new Thread (tg, "thread 1"); Thread t2 = new Thread (tg, "thread 2"); Thread t3 = new Thread (tg, "thread 3"); tg = new ThreadGroup ("subgroup 2"); Thread t4 = new Thread (tg, "my thread"); tg = Thread.currentThread ().getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Active thread groups in " + tg.getName () + " thread group: " + agc); tg.list (); } }

ThreadGroupDemo上の図に表示されているものを反映する適切なスレッドグループとスレッドオブジェクトを作成します。subgroup 1およびsubgroup 2グループがmainの唯一のサブグループであることを証明するにThreadGroupDemoは、次のようにします。

  1. の静的メソッド(メインスレッドのオブジェクトへの参照を返す)に続いてのメソッドをThreadGroup呼び出すことにより、メインスレッドのオブジェクトへの参照を取得します。ThreadcurrentThread()ThreadThreadThreadGroup getThreadGroup()
  2. 返されたばかりの参照でThreadGroupint activeGroupCount()メソッドを呼び出してThreadGroup、メインスレッドのスレッドグループ内のアクティブグループの推定値を返します。
  3. ThreadGroupString getName ()メソッドを呼び出して、メインスレッドのスレッドグループ名を返します。
  4. ThreadGroupvoid list ()メソッドを呼び出して、メインスレッドのスレッドグループとすべてのサブグループの標準出力デバイスの詳細を印刷します。

実行ThreadGroupDemoすると、次の出力が表示されます。

Active thread groups in main thread group: 2 java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Thread-0,5,main] java.lang.ThreadGroup[name=subgroup 1,maxpri=10] Thread[thread 1,5,subgroup 1] Thread[thread 2,5,subgroup 1] Thread[thread 3,5,subgroup 1] java.lang.ThreadGroup[name=subgroup 2,maxpri=10] Thread[my thread,5,subgroup 2]

のメソッドへの内部呼び出しのThread結果で始まる出力、パート1で説明した出力形式。その出力とともに、で始まる出力が表示されます。この出力は、スレッドグループの名前とそれに続く最大の優先度を識別します。list()ThreadtoString()java.lang.ThreadGroup

優先度とスレッドグループ

スレッドグループの最大優先度は、そのスレッドのいずれかが達成できる最高の優先度です。前述のネットワークサーバープログラムについて考えてみます。そのプログラム内で、スレッドはクライアントプログラムからの要求を待機して受け入れます。その前に、wait-for / accept-requestスレッドは、最初に、そのスレッドの優先度のすぐ下にある最大優先度のスレッドグループを作成する場合があります。その後、リクエストが到着すると、wait-for / accept-requestスレッドは、クライアントリクエストに応答するための新しいスレッドを作成し、以前に作成されたスレッドグループに新しいスレッドを追加します。新しいスレッドの優先度は、スレッドグループの最大値まで自動的に下がります。そうすれば、wait-for / accept-requestスレッドはより頻繁に実行されるため、要求に対してより頻繁に応答します。

Javaは、各スレッドグループに最大の優先順位を割り当てます。グループを作成すると、Javaはその親グループからその優先順位を取得します。その後、ThreadGroupvoid setMaxPriority(int priority)方法を使用して最大優先度を設定します。最大優先度を設定した後にグループに追加するスレッドは、最大値を超える優先度を持つことはできません。優先度の高いスレッドは、スレッドグループに参加すると自動的に低下します。ただし、を使用setMaxPriority(int priority)してグループの最大優先度を下げる場合、そのメソッド呼び出しの前にグループに追加されたすべてのスレッドは、元の優先度を保持します。たとえば、優先度8のスレッドを最大優先度9のグループに追加し、そのグループの最大優先度を7に下げると、優先度8のスレッドは優先度8のままになります。いつでもスレッドグループを決定できます。最大の優先順位は、呼び出すことによって、よThreadGroupさんint getMaxPriority()方法。優先度とスレッドグループを示すために、私はMaxPriorityDemo次のように書いています。

リスト2.MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo { public static void main (String [] args) { ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg maximum priority = " + tg.getMaxPriority ()); Thread t1 = new Thread (tg, "X"); System.out.println ("t1 priority = " + t1.getPriority ()); t1.setPriority (Thread.NORM_PRIORITY + 1); System.out.println ("t1 priority after setPriority() = " + t1.getPriority ()); tg.setMaxPriority (Thread.NORM_PRIORITY - 1); System.out.println ("tg maximum priority after setMaxPriority() = " + tg.getMaxPriority ()); System.out.println ("t1 priority after setMaxPriority() = " + t1.getPriority ()); Thread t2 = new Thread (tg, "Y"); System.out.println ("t2 priority = " + t2.getPriority ()); t2.setPriority (Thread.NORM_PRIORITY); System.out.println ("t2 priority after setPriority() = " + t2.getPriority ()); } }

実行MaxPriorityDemoすると、次の出力が生成されます。

tg maximum priority = 10 t1 priority = 5 t1 priority after setPriority() = 6 tg maximum priority after setMaxPriority() = 4 t1 priority after setMaxPriority() = 6 t2 priority = 4 t2 priority after setPriority() = 4

スレッドグループAtg参照)は、最高の優先度(10)を最大として開始します。スレッドXを持つ、Threadオブジェクトt1の参照は、グループに参加し、その優先順位として5を受け取ります。そのスレッドの優先度を6に変更します。これは、6が10未満であるため成功します。その後、setMaxPriority(int priority)グループの最大優先度を4に下げるように呼び出します。スレッドXは優先度6のままですが、新しく追加されたYスレッドは優先度として4を受け取ります。最後に、スレッドYの優先度を5に上げる試みは失敗します。これは、5が4より大きいためです。

Note:setMaxPriority(int priority) automatically adjusts the maximum priority of a thread group's subgroups.

In addition to using thread groups to limit thread priority, you can accomplish other tasks by calling various ThreadGroup methods that apply to each group's thread. Methods include void suspend(), void resume(), void stop(), and void interrupt(). Because Sun Microsystems has deprecated the first three methods (they are unsafe), we examine only interrupt().

Interrupt a thread group

ThreadGroupinterrupt()メソッドを使用すると、スレッドは特定のスレッドグループのスレッドとサブグループに割り込むことができます。この手法は、次のシナリオで適切であることがわかります。アプリケーションのメインスレッドは、それぞれが作業単位を実行する複数のスレッドを作成します。すべてのスレッドは、スレッドが結果を調べる前にそれぞれのワークユニットを完了する必要があるため、各スレッドはワークユニットの完了後に待機します。メインスレッドは作業状態を監視します。他のすべてのスレッドが待機すると、メインスレッドはinterrupt()他のスレッドの待機を中断するように呼び出します。次に、それらのスレッドは結果を調べて処理できます。リスト3は、スレッドグループの中断を示しています。

リスト3.InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup { public static void main (String [] args) { MyThread mt = new MyThread (); mt.setName ("A"); mt.start (); mt = new MyThread (); mt.setName ("B"); mt.start (); try { Thread.sleep (2000); // Wait 2 seconds } catch (InterruptedException e) { } // Interrupt all methods in the same thread group as the main // thread Thread.currentThread ().getThreadGroup ().interrupt (); } } class MyThread extends Thread { public void run () { synchronized ("A") { System.out.println (getName () + " about to wait."); try { "A".wait (); } catch (InterruptedException e) { System.out.println (getName () + " interrupted."); } System.out.println (getName () + " terminating."); } } }