このページで解決しない高度なJavaのシステム障害はJaTSにお任せください

Trouble 7: スレッドに関する問題



<Q7-1>lockオブジェクトで待機中のスレッドを再開するために、lockに対してnotify()しても再開する場合と停止したままの場合があります。


<発生環境>
OS RedHatLinux6.1/6.2
JDK JDK1.3
Vender Sun
<A7-1>
1つのlockオブジェクトに対して、複数のThreadがwait()していることが原因です。
lockで一度に待機するThreadを1つにすることで解決できます。
また、lockに複数のThreadが待機している場合、lock.notify()で処理を再開するThreadは任意となることに注意してください。
Page Top

<Q7-2>Socketでreadブロック中にclose()を呼んでも回線が切断されません。


<発生環境>
OS Solaris2.5.1
JDK JDK1.2.1
Vender Sun
<A7-2>
readブロックしているスレッドに、interrupt()してからclose()を呼ぶ事で解決できます。
Page Top

<Q7-3>サウンドを連続して再生しているとアプリケーションが操作不能になりました。

マシンが高負荷である状態で、サウンド鳴動が停止せずアプリケーションが操作不能になりました。アプリケーションを再起動すると回復します。

<発生環境>
OS Windows NT/2000/XP
JDK JDK1.3
Vender Sun
<A7-3>
サウンドを鳴らしているスレッドとJavaSoundのイベントディスパッチスレッド (JavaSound EventDispatcher)がデッドロックしている可能性があります。
サウンドを鳴らしているスレッドを常に監視し、サウンドファイルが終了した時点で明示的にサウンドの再生を終了させるようにしてください。
この対処で、JavaSound EventDispatcherスレッドがデッドロックすることを回避できます。(サウンドファイルの終了はClipインタフェースのisRunning()メソッドで確認できます)
Page Top

<Q7-4>プログラムが何も出力しないで止まってしまいます。

私のプログラムが止まっている様です。コンソールにもログファイルにも何も出力しないため、何が起こっているかわかりません。
解析方法を教えてください。

<発生環境>
OS Any
JDK JDK1.2以降
Vender Sun
<A7-4>
デッドロックが発生している可能性が高いです。まずスレッドダンプを取得しましょう。

スレッドダンプを取得する方法は次の通りです。

○Windowsの場合:
  java.exeが動作しているDOS窓上で、Ctrl+Breakを押します。
  (javaw.exeでは取得できません)

○UNIX系OSの場合:
  対象のJavaプロセスにQUITシグナルを送信します。
  $ kill -QUIT <pid>

この操作により、スレッドダンプがJavaVMの標準エラー出力に表示されます。

J2SE1.4.1からは HotSpotVMにデッドロック検出ユーティリティが追加され、モニターオブジェクトをロックし合うような単純デッドロックの場合、スレッドダンプと共にJavaVMが警告を発してくれます。また複雑なデッドロックやライブロックが発生している場合でも、スレッドダンプを解析することでロック箇所を特定することができるでしょう。

以下はJ2SE1.4.1_01-b01によるスレッドダンプの出力例です。

Full thread dump Java HotSpot(TM) Client VM (1.4.1_01-b01 mixed mode):

"DestroyJavaVM" prio=5 tid=0x00034788 nid=0x764 waiting on condition [0..7fadc]

"Thread-2" prio=5 tid=0x00A12430 nid=0xaf4 waiting for monitor entry [acef000..acefd8c]
        at sample.deadlock.DeadLock.lock(DeadLock.java:29)
        - waiting to lock <02AA3B28> (a java.lang.Object)
        at sample.deadlock.DeadLock.run(DeadLock.java:47)
        - locked <02AA3B30> (a java.lang.Object)

"Thread-1" prio=5 tid=0x00A12EB0 nid=0x5cc waiting for monitor entry [acaf000..acafd8c]
        at sample.deadlock.DeadLock.lock(DeadLock.java:29)
        - waiting to lock <02AA3B30> (a java.lang.Object)
        at sample.deadlock.DeadLock.run(DeadLock.java:47)
        - locked <02AA3B28> (a java.lang.Object)

==== 中略 ====

"Suspend Checker Thread" prio=10 tid=0x009A8530 nid=0xb08 runnable

Found one Java-level deadlock:
=============================
"Thread-2":
  waiting to lock monitor 0x9a4b7c (object 0x2aa3b28, a java.lang.Object),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 0x9a4b5c (object 0x2aa3b30, a java.lang.Object),
  which is held by "Thread-2"

Java stack information for the threads listed above: 
=================================
"Thread-2":
        at sample.deadlock.DeadLock.lock(DeadLock.java:29)
        - waiting to lock <02AA3B28> (a java.lang.Object)
        at sample.deadlock.DeadLock.run(DeadLock.java:47)
        - locked <02AA3B30> (a java.lang.Object)
"Thread-1":
        at sample.deadlock.DeadLock.lock(DeadLock.java:29)
        - waiting to lock <02AA3B30> (a java.lang.Object)
        at sample.deadlock.DeadLock.run(DeadLock.java:47)
        - locked <02AA3B28> (a java.lang.Object)

Found 1 deadlock.
上の例では、"Thread-1"と"Thread-2"がlock()メソッドでロックしており、そのデッドロックをJavaVMが検出/指摘していることが分かります。

参考)
自分のプログラムでスレッドを生成するときには、名前をつけることをお勧めします。
スレッド名称はデフォルトでは「Thread-<連番>」となり、スレッドダンプの解析が非常に困難になります。
Page Top

<Q7-5>Concurrent APIを利用して、スレッドプールを実装しましたが、並列に動いてくれません。

JDK1.5以降で導入されたConcurrent APIを利用して、スレッドプールを実装し、
3つの処理を並列処理させようとしましたが、
以下のように、それぞれのタスクが順次に実行されます。

Task0-0 Start (31)
Task0-1 Start (1046)
Task0-2 Start (2060)
Task1-0 Start (3075)
Task1-1 Start (4090)
Task1-2 Start (5105)
Task2-0 Start (6120)
Task2-1 Start (7134)
Task2-2 Start (8149)

<発生環境>
OS Any
JDK JDK5.0以降
Vender Sun
<A7-5>
利用するAPIが間違えている可能性が高いです。
ExecutorsクラスのnewSingleThreadExecutorメソッドを利用して
ExecutorServiceクラスを作成すると、
単一スレッドでタスクを実行することになります。

複数スレッドでタスクを並列実行する場合には、
同時実行可能なスレッド数を指定するnewFixedThreadPoolメソッドや、
一定数のスレッドを再利用しながら処理するnewCachedThreadPoolメソッドを利用してください。

[newSingleThreadExecutor]
newSingleThreadExecutor


[newFixedThreadPool]
newFixedThreadPool

使うメソッドを変更した結果、以下のように動作をするようになりました。
Task0-0 Start (203)
Task1-0 Start (203)
Task2-0 Start (204)
Task1-1 Start (1218)
Task0-1 Start (1218)
Task2-1 Start (1219)
Task1-2 Start (2233)
Task0-2 Start (2233)
Task2-2 Start (2234)
参考:
今回検証に利用したサンプルコードを以下に掲載します。
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int index = 0; index < 3; index++){
  executor.execute(new Task(index, start));
}
executor.shutdown();
public class Task implements Runnable{
  int  index_;
  long start_;

  public Task(int index, long start)  {
    this.index_ = index;
    this.start_ = start;
  }

  public void run(){
    for (int index = 0; index < 3; index++){
      long time = System.currentTimeMillis() - this.start_;
      System.out.println("Task" + this.index_ 
                   + "-" + index + " Start (" + time + ")");
      try{
        Thread.sleep(1000);
      }catch (InterruptedException ignore){
        ignore.printStackTrace();
      }
    }
  }
}

Page Top

<Q7-6>ブロック処理に対応したQueueを利用していますが、意図通り動いてくれません。

JDK1.5以降で導入されたLinkedBlockingQueueを用いて、
以下のような処理を実行したいと考えましたが、
処理を待ち受けてくれません。

実行結果のように、Serverが処理を待ち受けずにnullを取得してしまいます。

LinkedBlockingQueueを用いた処理

[実行結果]

Server handles null
Client requests [ Request No. 0 ]
Client requests [ Request No. 1 ]
Client requests [ Request No. 2 ]
Client requests [ Request No. 3 ]
Server handles  [ Request No. 0 ]
(略)
Client requests [ Request No. 410 ]
Server handles  [ Request No. 408 ]
Client requests [ Request No. 411 ]
Server handles  [ Request No. 409 ]
Server handles  [ Request No. 410 ]
Server handles  [ Request No. 411 ]
Server handles  null
Client requests [ Request No. 412 ]

[サンプルコード]
RequestQueue queue = new RequestQueue();
new ClientThread(queue).start();
new ServerThread(queue).start();
public class RequestQueue{
  private BlockingQueue queue_ = new LinkedBlockingQueue();

  public Request getRequest(){
    Request req = queue_.poll();
    return req;
  }

  public void putRequest(Request request){
    queue_.offer(request);
  }
}
※ServerThread、ClientThreadはそれぞれ、
 RequestQueueのgetRequest()、putRequest()をランダム間隔で呼び続けるThreadクラス。

<発生環境>
OS Any
JDK JDK5.0以降
Vender Sun
<A7-6>
ブロックされないメソッドを利用しています。

LinkedBlockingQueueは、ブロック処理を考慮したQueueクラスですが、
ブロック処理が有効なのは、BlockingQueueインタフェースで定義された以下の2つのメソッドのみです。

・take():キューの先頭から要素を取り出す
・put():キューの末尾に要素を追加する


そのため、キューの先頭を取得および削除するpollメソッドや、
指定された要素を追加するofferメソッドを利用して、
LinkedBlockingQueueオブジェクトにアクセスしても、
ブロックされません。

上記のサンプルコードの場合、RequestQueueクラスを以下のように修正すれば、
この問題を解決することができます。
public class RequestQueue{
  private BlockingQueue queue_ = new LinkedBlockingQueue();

  public Request getRequest(){
    Request req = null;
    try{
      req = queue_.take();
    }catch (InterruptedException ignore){
      ignore.printStackTrace();
    }
    return req;
  }

  public void putRequest(Request request){
    try{
      queue_.put(request);
    }catch (InterruptedException ignore){
      ignore.printStackTrace();
    }
  }
}
この修正の結果、実行結果は以下のようになりました。
待ち受け処理が成功していることが分かります。

[実行結果]
Client requests [ Request No. 0 ]
Server handles  [ Request No. 0 ]
Client requests [ Request No. 1 ]
Client requests [ Request No. 2 ]
Client requests [ Request No. 3 ]
Server handles  [ Request No. 1 ]
(略)
Server handles  [ Request No. 430 ]
Client requests [ Request No. 432 ]
Server handles  [ Request No. 431 ]
Client requests [ Request No. 433 ]
Server handles  [ Request No. 432 ]
Client requests [ Request No. 434 ]

Page Top

注意:本文書の内容に誤りがあり、またこの文書によって不利益を被っても、
Acroquest Technology 株式会社は一切関知いたしません。

  • 現象別Index
  • 原因別Index

Find Bugsバグ詳細

Find Bugs Bug Descriptions日本語版

RSSで更新情報を取得する

RSSとは、ホームページの更新情報を配信する為のフォーマットです。
RSSを利用すると、登録したページの情報が更新された場合に、更新情報を自動的に受け取る事ができます。

詳細

弊社小森が執筆致しました

Javaでオブジェクト指向開発

Javaプログラミング言語習得において、新人プログラマーの最初の障害は「オブジェクト指向の壁」です。
本書は、Javaのソフトウェア開発を中心に事業を発展させてきたAcroquest社の新人教育セミナーを加筆・書籍化したもので、大卒の新人に対して、ゼロからJava言語を教えてきた実績をフィードバックしています。

メールマガジン配信中

Javaトラブルシューティングのメルマガをはじめました!是非ご購読ください

詳細