13066


私が投稿した類似質問  
[【Java】Threadの割り込みについて](https://ja.stackoverflow.com/questions/9169/java-
thread%e3%81%ae%e5%89%b2%e3%82%8a%e8%be%bc%e3%81%bf%e3%81%ab%e3%81%a4%e3%81%84%e3%81%a6)

勘違いがあったので修正します。

[code]

    public class Main extends Thread{
        private Timer timer;
    
        public static void main(String[] args) {
            try {
                Main main = new Main();
                main.start();　// --- 【1】
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
            timer = new Timer();
            timer.setObserver(this);
            timer.setDaemon(true);
            try {
                timer.run();
    
                sleep(3000);
            } catch (InterruptedException e) {
                System.out.print("割り込み");
            }
        }
    }
    
[/code]

[code]

    public class Timer extends Thread {
    
        private Thread observer;
    
        public void setObserver(Thread observer) {
            this.observer = observer;
        }
    
        @Override
        public void run() {
            try {
                System.out.println("THREAD COUNT");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("THREAD FINAL");
                observer.interrupt();
            }
        }
    }
    
[/code]

あくまで例です。

ちょっと勘違いがあったので、訂正しました。  
上記だと、`Main` を `Thread` (以下、 `MainThread` と表記)として稼働することで、 `SubThread`
からの割り込みが可能かと思います。

この時、 `MainThread` の終了を待って、 `main()` で終了する準備をしたいのですが、  
`MainThread` の終了を実行クラスに通知することは可能でしょうか？  
**"// --- 【1】"** 部分の後に、データの記録等行いたいなと思っています。


CC BY-SA 3.0
2015-05-15T11:51:43.433
10233
2015-08-05T09:57:16.513
2017-04-13T12:52:38.920
-1
8396
question
java
Threadの待機


色々と書かれていますが、類似質問の内容も加味すると、要は「タイムアウトを設定できるバックグラウンドタスクを実装したい」という理解で良いでしょうか？
なおかつ「バックグラウンドタスクが失敗した場合は後始末をした後にメインスレッドも終了させる」ということですよね？

Threadクラスを直接使わなければならないというような制約がないのであれば、Java5以降に標準で付いてくる[並行処理ユーティリティー](http://docs.oracle.com/javase/jp/6/technotes/guides/concurrency/overview.html)を使うべきです。今やThreadクラスを直接触るのはアセンブラでプログラミングするようなものであり、お勧めしません。

## 実装サンプル：

質問で例示されているのとは若干形が違いますが、バックグラウンド処理をタイムアウト指定で実行するサンプルを作ってみました。  
タスクには戻り値があるという前提での基本的な流れとしては、

  1. `ExecutorService`を生成
  2. `Callable`なタスクのインスタンスを生成
  3. `ExecutorService#submit`でタスクを実行（バックグラウンドスレッドを開始）
  4. `Future#get`で戻り値が返るまで待つ
  5. `future`への後始末を行う
  6. `ExecutorService`を終了する

という形になります。  
タスクには戻り値がない（タスクが実行する副作用のみが必要である）場合は、タスクを`Runnable`なものと実装した上でやはり`ExecutorService#submit`を実行します。この場合
`Future#get`は`null`を返します。

なお`TimeoutException`節と`finally`節の両方で`future.cancel()`を実行していますが、`Future#cancel`は複数回呼び出されても支障はありません。また正常終了した`Future`に対して`cancel()`しても何も起こりません。

### Main.java

[code]

    public class Main {
        public static void main(String[] args) {
            // バックグラウンドタスク用のスレッドプールを生成
            ExecutorService execService = Executors.newSingleThreadExecutor();
    
            Main main = new Main();
            main.executeTask(execService);
            System.out.println("\n[FINISH]");
            execService.shutdown();
        }
    
        private void executeTask(ExecutorService service) {
            Future<Integer> future;
            Integer result;
    
            // タスク実行
            future = service.submit(new CallableTask());
            try {
                // future.get で結果が返るまで待つ。ただし100ミリ秒でタイムアウトとする
                result = future.get(100L, TimeUnit.MILLISECONDS);
                System.out.printf("\nResult : [%d]\n", result);
            } catch (InterruptedException e) {
                // 何らかの割り込みが発生したとき
                System.out.println("\nInterrupted.");
                e.printStackTrace();
            } catch (ExecutionException e) {
                // タスクの中で例外が発生したとき
                System.out.println("\nException.");
                e.printStackTrace();
            } catch (TimeoutException e) {
                // タイムアウトしたとき
                future.cancel(true);  // 即座にタスクにキャンセル通知
                System.out.println("\nTimeout.");
                e.printStackTrace();
            } finally {
                future.cancel(true);
            }
        }
    }
    
[/code]

### CallableTask.java

[code]

    /**
     * バックグラウンドで実行されるタスクの定義
     * 乱数を生成して4095の倍数を探す
     * ただし32767の倍数だと例外をスローする
     */
    public class CallableTask implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            int x;
            do {
                // 割り込みチェック : Future#cancel(true) を検知する
                if (Thread.currentThread().isInterrupted()) {
                    x = 0;
                    break;
                }
                x = ThreadLocalRandom.current().nextInt();
                System.out.printf("#");
                if ((x % 32767) == 0) {
                    throw new RuntimeException(String.format("BAD NUMBER : %d", x));
                }
            } while ((x % 4095) != 0);
            return x;
        }
    }
    
[/code]


CC BY-SA 3.0
2015-08-05T07:50:11.213
13066
2015-08-05T09:57:16.513
2015-08-05T09:57:16.513
10492
10492
10233
answer