Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

マルチスレッドについて

[環境]
Swing
Oracle9i

[仕様]
DB登録件数をリアルタイムでJDialogを継承した画面(進捗画面)に表示させる。
進捗画面で「キャンセル」ボタンを押下すると、DB登録を止める。←ここが困っている個所

[困っている個所の説明]
swingはシングルスレッド設計のため、
メインスレッド、サブスレッド(進捗画面表示)、サブスレッド(DB登録)に
分けました。
DB登録、進捗表示はできるのですが、進捗画面で「キャンセル」ボタンを押下した
場合のサブスレッド(DB登録)への割り込みが出来ません。
そもそも、こういったことが出来ないのか、考え方が違うのか・・・
手詰まりになりました。
どなたかご教授のほど、よろしくお願いします。

[簡易ソース]
※現在、開発環境がなく即席でサンプルソースを書きましたので
 文法上で何か間違いがあったらすみません。
 なんとなくやりたいことが理解してもらえると助かります。

// メインスレッド
public class JDialogTest extends JDialog{

 // コンストラクタ
 public JDialogTest(){
  button = new jButton("OK");
  button.addActionListener(this);
 }

 // OKボタン押下時に登録スレッド(内部クラス)起動
 public void actionPerformed(ActionEvent e){
  EntryThread entryThread = new EntryThread();
 }

 // 登録スレッド(内部クラス)
 private class EntryThread extends Thread{

  // コンストラクタ
  public EntryThread(){
   this.start();
  }

  run(){
   try{
    // 進捗画面起動
    // 進捗画面で割り込みたいため、内部クラスを渡す
    JDialogProc jDialogProc = new JDialogProc(this);
    jDialogProc.show();

    // DB登録スレッド起動
    ThreadEntry threadEntry = new ThreadEntry(iEntryCnt);

   catch(InterruptedException e) {
    // キャンセルボタンを押された時にDB登録スレッドも割り込む
    // ※ここが問題でどうやら割り込めてない模様・・・
    threadEntry.interrpt()
   } catch(Exception e){
    〜略〜
   }
  }
 }
}

//進捗画面
public class JDialogProc extends JDialog{

 // メンバ変数
 Thread m_therad = null; //登録スレッド

 // コンストラクタ
 public JDialogProc(Thead thread){
  m_thread = thread;
  button = new jButton("Cancel");
  button.addActionListener(this);
 }

 // キャンセルボタン押下で割り込み発生させて
 // 登録スレッド(内部クラス)でcatch
 public void actionPerformed(ActionEvent e){
  m_thread.interrpt()
 }
}

//DB登録スレッド
public class ThreadEntry extends Thread{

 // メンバ変数
 // 登録件数
 int m_iEntryCnt = 0;

 // コンストラクタ
 public ThreadEntry(int iEntryCnt){
  m_iEntryCnt = iEntryCnt;
  this.start();
 }

 run(){
  try{
   // 登録件数分登録
   for(int i = 0; i < m_iEntryCnt; i++){
    〜略 DB登録〜
   }
  } catch (Exception e){
   // 登録スレッドで割り込み命令を行っても
   // 例外処理に来てくれない・・・
  }
 }
}

4

回答

82511

閲覧

4件の回答

評価

30

まず原因を切り分けて下さい。
可能性 (他にもあるかもしれません)
A: DB登録処理が割り込みを独自に処理して呼び出し元に返さない。
B: DB登録処理が割り込みを感知しない。
C: Swing のイベントディスパッチスレッドがビジーでボタンが押せない。
D: Swing の操作をイベントディスパッチスレッド以外で行ったため誤動作してる。
対処
A: 割り込みは使えないでしょうからフラグでもたてて下さい。
B: 登録のループ内で interrupted か isInterrupted で確認して下さい。
C/D: スレッドの使い方の間違えを直して下さい。

評価

0

回答ありがとうございます。

>C: Swing のイベントディスパッチスレッドがビジーでボタンが押せない。
キャンセルボタンは押せており、run()メソッド内でcatch出来ています。

>D: Swing の操作をイベントディスパッチスレッド以外で行ったため誤動作してる。
Cが出来ているため、恐らくこれも違う?

となると、
>A: DB登録処理が割り込みを独自に処理して呼び出し元に返さない。
>B: DB登録処理が割り込みを感知しない。
上記が濃厚だと思うのですが、

>B: DB登録処理が割り込みを感知しない。
実は、DB登録処理でisInterruptedを埋めて確認していたのですが、
どうも割り込まれていないようなのです。

そうすると、Aか・・・。
DB登録処理でcatchされていないか確認してみます。
念のため、Bも再度確認してみます。

評価

0

頑張って下さい。
効果的なきり分けとしてはDB登録の代わりに何か単純な処理を呼んでみるといいかも。
ただし Thread.sleep 呼んで例外をほかさないように。

評価

0

> DB登録処理でisInterruptedを埋めて確認していたのですが、
> どうも割り込まれていないようなのです。

割り込み自体はおこなわれているはずで、Thread.isInterrupted()で確認をおこなおうとしていることに問題があります。

割り込みステータスを確認するThread.isInterrupted()メソッドとは別に、割り込み発生を検査するinterrupted()メソッドが存在することにも注意してください。(このinterrupted()メソッドが落とし穴になっています。)

スレッドへの割り込みが発生した場合、割り込み可能な処理が割り込みを処理する可能性があります。割り込み可能な処理の代表例は Thread.sleep() です。

あるスレッドが Thread.sleep() を実行して待機している間に割り込みが発生した場合どうなるか見てみましょう。割り込みがおこなわれた瞬間は Thread.isInterrupted()はtrueになっているはずです。このとき、Thread.sleepはinterrupted()メソッドを呼び出して割り込みが発生したかどうかを検査します。interrupted()メソッドで割り込みが発生していることが検出された場合Thread.sleep()はInterruptedExceptionを投げられます。

ここで重要なのはinterrupted()検査メソッドを呼び出すと、割り込みステータスがクリアされる、ということです。簡単にいうと、Thread.sleep()メソッドが割り込みを処理したので、割り込みステータスをクリアして、割り込まれたことをInterruptedExceptionで通知した、ということになります。

つまり、割り込みステータスは割り込み可能な処理によってクリアされることがあるということを忘れてはいけません。行儀の良い割り込み可能な処理は、代わりにInterruptedExceptionを投げるはずです。

スレッドが割り込まれたどうかの判断は、isInterrupted()ステータスだけでなく割り込み例外(InterruptedException、InterruptedIOException)が発生したかどうかも見なければなりません。

割り込み可能な処理が存在しない(interrupted()検査メソッドが呼ばれない)、いわゆるビジーループの場合には割り込み例外が発生しない代わりに、isInterrupted()ステータスがtrueの状態を維持し続けることになります。この場合はあなたがinterrupted()検査メソッドを書いて割り込みステータスをクリアすべきです。

今回はデータベース処理ということなので、割り込み可能なI/O処理で割り込み例外が正しく発生しているのではないかと思います。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。