Java 標準入力待ちでスレッドをブロックさせない方法

投稿者: | 2014年12月11日

こんにちはSayahamittです。

 

Javaでマルチスレッドなコードを書いている時に、あるスレッドでユーザーから標準入力待ちをする必要がありました。さらに、そのスレッドは他のスレッドと連動していて、他方のスレッドが終了したらそのスレッドも即座にかつ自動的に終了しなければなりません。

 

ご多分に漏れず僕はソッコーで躓きました。二日間も。

 

問題になったのはSystem.inを読むBufferdReaderでreadline()を行うと標準入力待ちになり、ユーザーがEnterキーを押してくれるまでそのスレッドの終了はおろか、interrupt()も出来ないことです。

しかし、僕が想像もしなかったスマートな解決策がありました。

 

今回の問題は、ユーザーから連続して入力を待ち受けるスレッドを如何に外部から終了させるか、です。

多少経験があればまず躓く所では無いのでしょうが、つい最近講義でJavaを触り始めた僕は完全にハマりました。

 

さて、当初僕が書いていたコードは大筋として以下の通りです。

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

class InputThread extends Thread{
	private boolean _halt = false;
	private String data = null;

	public InputThread(){
	}

	public void Halt(){
		_halt = true;
	}

	public String getInputdata(){
		return data;
	}

	public void run(){
		try{
			//キーボードの入力ストリームを取得
			BufferedReader com = new BufferedReader(new InputStreamReader(System.in)); 
			
			while(_halt != true){
				data = com.readLine();
			}

			System.out.println("入力受付スレッドを終了します。");
		}catch(Exception e){
			System.out.println(e.toString());
			return;
		}
	}

	public static void main(String[] args)
    {
        InputThread inputthread = new InputThread();
        inputthread.start();

        try{
            Thread.sleep(5000);
        }catch(InterruptedException e){
			System.out.println(e.toString());
        }
        inputthread.Halt();

    }
}

このコードではInputThreadクラスのインスタンスを別スレッドで実行し、5秒経ったら終了することを期待しています。

しかし、実際にはこのコードを実行して5秒放置しても自動的には終了されません。5秒経過後ユーザーが何か入力を行ってEnterキーを押した後に終了されます。

まぁ確かに5秒経った後初めてEnterが押された直後に終了されはするので、どうでもいいコードならこれで終わりにしても良いかもしれません。だが、しかしです。今回はHalt()が実行されたら即座に終了して欲しいのです。(´・ω・`)

 

そもそも、なぜ標準入力待ちになっているスレッドを外部から即座に終了出来ないかと言えば、標準入力バッファーが空の間はSystem.inを読むBufferdReaderのreadline()メソッドが値を返せないからです。readline()メソッドは標準入力バッファーから何か値を取得できるまで待つしかないのです。

 

「じゃぁさ、標準入力バッファーが空の時はreadline()しなければいいじゃん。」

stackoverflowのとあるページが教えてくれました。

 

具体的には、上記のコードの25行目、run()メソッドのwhile文の中身を以下のように変更します。

			while(_halt != true){//上記コード25行目
				if(com.ready()){
					data = com.readLine();
				}
				
				sleep(100);
			}

BufferdReaderクラスのready()メソッドを利用して、標準入力バッファーの中身が空でないことを確認してからreadline()メソッドを実行しています。こうすることで、readline()メソッドがユーザーの入力を待って停止してしまうことを防いでいるわけです。

また、この処理はビジーウェイトになるのでsleep()メソッドで過剰にCPUリソースを消費するのを抑制しています。

 

 

たったこれだけの事です。

ready()メソッドを知っていれば躓かない所なんでしょうけどね…(´・ω・`)

 

他にもっと良い方法があるかも知れません。見つけたらまた書きます。

 

ではでは、ノシー

 

丸ぱくr参考にしたページ

stackoverflow Java: how to abort a thread reading from System.in

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください