ユーザインタフェースを作る際、よく使うマルチスレッドの技

 

画面のイベント処理、たとえば、ボタンを押したときの処理に時間がかかる場合、ボタンを押してから処理が終わるまで、画面がフリーズしてしまいます。それを防ぐために、イベントの処理をスレッドで行います。

 

(本サイト入口にある)マルチスレッド版CPUエミュレータでは、知らないと自力では作れそうにないスマートな構造が使われています。(mainスレッドを引数にしたり、実行中のスレッドの属するクラスのメソッドを読んだりしています)

 

そこで、マルチスレッド版CPUエミュレータの骨格を抽出した簡単なプログラムを作りました。

 

簡単なプログラムの構造:

スレッドは次の3つです(1)MAINスレッド、(2)INスレッド、(3)OUTスレッド。

MAINは入力要請をINスレッドに出す。INスレッドはキーボードから文字列を入力してもらい、1行入力されるとMAINに知らせる。次にMAINは入力された文字列をOUTに出力してもらう(byeと入力すると終了)。

 

スレッド間の同期はQueue (Q) を用いて実現する:

(このプログラムのアイデアはQを各スレッド付属させ、Qへの要請を各スレッドのメソッド(put)に頼んでいる点です)

 

各スレッドは自分のQを見にゆき、Qに要請がなければWAITになる。WAITになっているスレッドは要請が来たときNOTIFYWAITから解放される。

 

本プログラム(3スレッド)の構成をもう少し細かく追ってみる:

Step0MAINに空の要請を送って起動する

Step1MAININに入力要請を出す。

要請はINのQに送られるが、MAININのQに直接送るのではなく、INputメソッドに頼んで送ってもらう。INはQをウオッチしていて入力要請があれば入力作業を行い、(INは入力文字列をMAINmGrに書き込む)、終了をMAINのQに伝える(MAINputを経由)。

MAININに要請を出した後、自分のQを見にいくと空なのでWAITになる。

Step2INから入力終了情報がMAINのQに届くとWAIT状態のMAIN(NOTIFY)目を覚ます。

Step3MAINOUTに出力依頼をする(OUTのQに依頼を送る)OUTは最初自分のQを見にいって空なのでWAITになっているが、MAINからの要請で目を覚まし出力を行う。

出力終了後はMAINのQにOUTという終了信号を送る。

Step4step1に戻る。

“bye”が入力されると、MAINは自分のQにEXITを送り、MAINではQにEXITが来ていると終了する。

 

 

MAIN

 

 

watch

OUT

 

 

watch

IN

 

 

watch

put

put

入力要請

出力依頼

出力完了

入力完了

put

 

 

 

 

 

 

 

 

 

 


===

import java.io.BufferedReader;

import java.util.List;

import java.util.ArrayList;

import java.io.InputStreamReader;

 

////メインクラス////

 

public class Cmain extends Thread {

 

    //InOutのデータ受け渡し場所

    private String mGr= null;

    //オブジェクト実行待ち行列

    private Q mQ = new Q();

    private In mInThread= null;

    private Out mOutThread= null;

 

    // メインメソッド

 

    public static void main( String[] pArgs ) {

 

        Cmain cMain = new Cmain();

        cMain.start();//mainスレッド実行

        cMain.put( new String() );

    }

 

    private Cmain() {

        // INOUTの各処理スレッドを開始

        mInThread = new In( this );

        mOutThread = new Out( this );

    }

 

     //mainスレッド

 

    public void run () {

        String aStrValue = null;

        int inData=0;

 

        try{

        // 割り込まれるまでループ

           while (true) {

                // キューからイベントを取得

                aStrValue = (String) mQ.get();

 

                if ( aStrValue.equals( "EXIT" ) ) {

                    System.exit(0);

                }

                

                if(!aStrValue.equals( "IN" ))

                               // IN

                               mInThread.Input();

                            

                else{

 

                   if(mGr.equals("bye"))  mQ.put( "EXIT" );

                               // OUT

                   mOutThread.put( mGr);

                }

           }//whileEnd

 

       }catch(Throwable t){}

           

    }//runEnd

 

   

    //mQueueに処理結果を送る

    public void put( String pStr ) {

       

        mQ.put( pStr );

    }

 

    //mGrに文字列を書込む。

    void setGr(String pStr ) {

 

        mGr= pStr;

    }

 

}

 

////IN入力スレッド////

 

class In extends Thread {

 

     //メインクラス

    private Cmain mMain = null;

 

     //オブジェクト実行待ち行列

    private Q iQ = new Q();

 

    //コンストラクタ

 

    public In( Cmain pMain ) {

       

        mMain = pMain;

        iQ = new Q();

        this.start();

    }

   

    public void run() {

 

        String aInput = null;

        BufferedReader aBufferedReader = null;

       

        while ( true ) {

 

            try {

                // キューからイベントを取得

                aBufferedReader = (BufferedReader) iQ.get();

               

                aInput = aBufferedReader.readLine();

                   

                mMain.setGr( aInput );

               

                mMain.put( "IN" );

 

            }   catch ( Exception e ) {}

        }

    }

 

   

 

     //データ入力Input処理

 

    void Input() {

 

        // 標準入力

        BufferedReader input

            = new BufferedReader(new InputStreamReader(System.in));

        

        iQ.put( input );

    }

}

 

 

////OUT処理クラス////

 

class Out extends Thread {

 

    //メインクラス

    private Cmain mMain = null;

 

     //オブジェクト実行待ち行列

    private Q oQ = new Q();

 

     // コンストラクタ(mainが引数)

 

    public Out( Cmain pMain ) {

 

        mMain = pMain;

        oQ = new Q();

        this.start();

    }

 

    public void run() {

 

        String aOutput = null;

       

        while ( true) {

 

            try {

               

                // キューからイベントを取得

                aOutput = (String) oQ.get();

               

                System.out.println( new String(aOutput.toString()) );

               

                mMain.put( "OUT" );

            }

            catch ( Throwable t ) { break;}

        }

    }

 

    //OUTPUTする文字列を格納する。

 

    void put( String pString ) {

 

        oQ.put( pString );

    }

}

 

 

////待ち行列のクラス////

 

class   Q {

 

    // 待ち行列リスト

    private List mQueue = null;

 

    //初期設定を行う

 

    public  Q() {

       

        mQueue = new ArrayList();

    }

 

 

    //pObj  オブジェクトをQueueに入れる

 

    public synchronized void put( Object pObj ) {

       

        // オブジェクトを追加する

        mQueue.add( pObj );

 

        // waitで待っているスレッドに通知(走らせる)

        if( mQueue.size() == 1 ) {

           

            notify();

        }

    }

 

    //  Queueからオブジェクトを取り出す。

    // オブジェクトがない場合、スレッドはwaitで待つ

 

    public synchronized Object get()

        throws InterruptedException {

       

        Object aObjRet = null;

       

        while ( true ) {

           

            // 待ち行列バッファにオブジェクトが溜まるまで待つ

            if ( mQueue.isEmpty() ) {

               

                wait();

            }

           

            if ( mQueue.isEmpty() ) {

               

            // 解放後にもなければ待ち状態に移行

                continue;

            }

           

            aObjRet = mQueue.get(0);

            mQueue.remove(0);

            break;

        }

       

        return aObjRet;

    }

}

 

===