Androidマルチスレッドアプリケーション

8606 ワード

Androidでは、マルチスレッドを実現する方法をいくつか提供しています.
1.Thread+Handlerはマルチスレッドを実現する:自分で新しく作ったThreadでUIを操作しないでください.Handlerを使用してプライマリスレッドと対話できます.以下の2つの使用方法がある:<1>.メッセージを送信:
// Handler       ,        
Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //  handler          ,  handler         ,    UI
    }
};
//     Thread
new Thread(new Runnable(){   
    public void run() {
        while(!Thread.currentThread().isInterrupted()) { 
            Message msg = handler.obtainMessage();  
            msg.what = XX;  
            msg.arg1 = XX;  
            msg.obj = XX;
            msg.sendToTarget();
            //        Message   handler     
        } 
    }      
}).start();

<2>. Runnableオブジェクトの送信:
// Handler       ,        
Handler handler = new Handler();
//     Thread
new Thread(){  
    public void run(){     
      while(!Thread.currentThread().isInterrupted()) {                 
            handler.post(new Runnable(){
                //        Runnable   handler      
              public void run(){
                //    handler     ,  handler         ,    UI
                }
            });
        } 
    }                     
}.start();//      

上記の例は、メインスレッドにHandlerを作成し、サブスレッドで参照します.自分のThreadにHandlerを作成してプライマリスレッドと対話する場合は、new Handlerの前にLooperを付ける必要があります.prepare()は、作成後にLooperを追加します.loop().理由:プライマリ・スレッドのみがデフォルトでメッセージ・キューを持ち、カスタム・スレッドにはMessageQueueとLooperはありません.
<3>.Thread割込み問題:
stop()インタフェースはありますが、安全ではなく、異常が発生します.したがって,interrupt()を呼び出して割り込みフラグを設定することで,Threadのrun()関数ではループ文があればループ中にisInterrupted()を判定し,trueであればrun()関数をアクティブに脱退してスレッドを終了し,割り込みの目的を達成する.
<4>. 1組のスレッドの実行は無秩序であり,協同作業を実現したり,それぞれ独立して作業したりすることができる.
//複数のスレッドは同じTaskRunのインスタンスを共有し、コラボレーション作業を実現します.  TaskRun task = new TaskRun();//TaskRun Runnableインタフェースを実装するrun()関数
  new Thread(task,"t1").start();  new Thread(task,"t2").start();//複数のスレッドは、それぞれのインスタンスを初期化します.すなわち、それぞれの作業を行います.  MyThread myT1 = new MyThread();//MyThreadはThreadを継承し、自分でrun()関数MyThread myt 2=new MyThread()を実現する.  myT1.start();  myT2.start();
 
2.AsyncTask非同期タスク:
1つのインスタンスは1回のみ実行され、複数のexecuteが例外を放出します.したがって、いくつかのタスクを実行するにはnewのいくつかのインスタンスがあります.
2.3プラットフォームは以前executeを呼び出して、すべての任務は同時に実行して、それからデフォルトはシリアルに実行して、executeOnExecuteor(ExecuterService)Executeorsを呼び出すことしかできない.NewCachedThreadPool()は、デフォルトのスレッドプールではなく、カスタムを使用して同時実行を実現します.
短時間のマルチスレッドタスク(数分)に適しています.長時間のタスクを実行する場合は、Executor、ThreadPoolExecutor、FutureTaskを使用するとよいでしょう.<1>.AsyncTaskは抽象クラスであり、「HTTP要求のURLなどのタスク実行を開始する入力パラメータ」、「バックグラウンドタスク実行の進捗状況」、「バックグラウンド計算結果のタイプ」の3つの汎用タイプを定義している.使用されていなければVoidで代用できます.<2>.AsyncTaskで定義されたメソッド:execute(Params...params)は、非同期タスクを実行します.このメソッドをコードで呼び出し、非同期タスクの実行をトリガーする必要があります.onPreExecute()は、execute(Params...params)が呼び出された直後に実行され、バックグラウンドタスクを実行する前にUIにタグを付けるために一般的に使用されます.doInBackground(Params...params)は、onPreExecute()が完了するとすぐに実行され、比較的時間のかかる操作を実行するために使用され、入力パラメータを受信し、計算結果を返します.UIを更新することはできません.実行中にpublishProgress(Progress...values)を呼び出して進捗情報を更新するしかありません.onProgressUpdate(Progress...values)は、publishProgress(Progress...values)の呼び出し時に実行され、進捗情報をUIコンポーネントに更新する.onPostExecute(Result result)では、バックグラウンド操作が終了するとメソッドが呼び出され、計算結果がパラメータとしてメソッドに渡され、UIコンポーネントに直接表示されます.
<3>.スレッドの終了:
cancel()バックグラウンドスレッドの実行を終了し、isCanceled()で問い合わせることで、繰り返し停止を避けることができます.cancel()を実行すると、onCanceled()がコールバックされ、onPostExecute()はコールバックされません.タイムリーに中断したい場合は、doInBackground()でステータスビットを検出して運転を継続するかどうかを判断します.
3.スレッドプール:JavaがExecutorsを通じて提供する4種類のスレッドプールは、それぞれ:<1>.NewCachedThreadPoolはキャッシュ可能なスレッドプールを作成し、executeを呼び出すと以前に構築されたスレッドが再利用され(スレッドが使用可能である場合)、回収可能でない場合は新しいスレッドが作成されます.IDLEメカニズムがあり、スレッドがTIMEOUT(60 s)を超えると自動的に終了します.通常、生存期間が短い非同期タスクを実行するために使用されます.<2>. 新FixedThreadPoolは、スレッドの最大同時数を制御し、超過したスレッドがキュー内で待機する定長スレッドプールを作成します.IDLEメカニズムはなく、スレッドが明示的に閉じるまで、プール内のスレッドは常に存在します.多くの場合、安定で固定された正規の同時スレッドがあります.定長スレッドプールのサイズは、システムリソースに基づいて設定することが望ましい.Runtime.getRuntime().availableProcessors()<3>. 新ScheduledThreadPoolは、タイミングおよび周期的なタスク実行をサポートする定長スレッドプールを作成します.以下は3種類のタイマーの書き方と比較です.
Handler.postDelayedは実際にはメインスレッドで実行されていますが、Runnableを送信するためにThreadを新規作成することもできます.
Timerメカニズムでは,タイミングタスクを実行するために1つのスレッドのみがカプセル化され,異常が発生することがある.
ScheduledExecutorServiceはTimerよりも安全で、機能がより強く、最適化案です.
private static final int REGUEST_MESSAGE_TIME = 1000*60*60*2;//2  
 
//  Handler     ,   Runnable         
private Handler mTimerHandler = new Handler();
private Runnable mTimerRunnable = new Runnable() {
    @Override  
    public void run() {
        // 2       Runnable
        msgTimerHandler.postDelayed(this, REGUEST_MESSAGE_TIME);
    }  
};
 
// Timer + TimerTask   , Timer   ,             
private Timer mTimer = null;
private TimerTask mTimerTask = new TimerTask() { 
    @Override
    public void run() {
    }
};
 
//                 
private ScheduledExecutorService mScheduledExecutorService = null;
 
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //     Runnable
    msgTimerHandler.post(mTimerRunnable);
 
    mTimer = new Timer();
    //   2      TimerTask,    
    mTimer.schedule(mTimerTask, 0, REGUEST_MESSAGE_TIME);
 
    //         5   
    mScheduledExecutorService= Executors.newScheduledThreadPool(5);
    //     Runnable ,    2      Runnable
    mScheduledExecutorService.scheduleAtFixedRate(new Runnable() {  
        @Override  
        public void run() {   
        }  
    }, 0, 2, TimeUnit.HOURS); 
}
protected void onDestroy() {
    super.onDestroy();
    //     Handler target   Message Callback,      
    msgTimerHandler.removeCallbacksAndMessages(null);
 
    if ( mTimer != null ) {
        //        schedule,   Timer        
        mTimer.cancel();
        mTimer = null;
    }
 
    //               
    mScheduledExecutorService.shutdown();
}

<4>. 新SingleThreadExecutorは、一意の作業スレッドのみでタスクを実行し、すべてのタスクが指定された順序(FIFO、LIFO、優先度)で実行されることを保証する単一スレッド化されたスレッドプールを作成します.同期の問題を考慮する必要はありません.IDLEメカニズムはありません.データベース操作、ファイル操作、一括インストールの適用、一括削除の適用など、同時性には適していないがIOブロック性やUIスレッド応答に影響を与える可能性のある操作に使用できます.メリット:a.存在するスレッドを再利用し、オブジェクトの作成、消滅のオーバーヘッドを低減し、パフォーマンスが優れています.b.最大同時スレッド数を効果的に制御し、システム資源の使用率を高めると同時に、多すぎる資源競争を避け、渋滞を避けることができる.c.タイミング実行、定期実行、単一スレッド、同時数制御などの機能を提供する.スレッドを開く2つの方法:Futuresubmit(Runnable task)/*Futureでスレッドの実行状態を判断*/void execute(Runnable command)/*スレッドが正常に完了したかどうか判断できません*/
スレッドプールを閉じる方法:
shutdown()は、新しいタスクの受け入れを停止し、コミットされたタスクの実行が完了するまで待機します(コミットされたタスクは、すでに実行されているクラスと、まだ実行が開始されていないクラスの2つに分類されます).コミットされたすべてのタスクの実行が完了すると、ExecutorServiceが閉じます.shutdownNow()は、実行中のすべてのアクティブなタスクを停止しようとし(対応するスレッドのinterrupt()を呼び出し、割り込みフラグビットを設定しただけ)、待機中のタスクの処理を一時停止し、実行待ちのタスクリストに戻ります.
スレッドプールのタスクの割り込み:ExecutorServiceにタスク呼び出しsubmitメソッドをコミットした後、戻り値はFutureオブジェクトであり、このオブジェクトのcancelメソッドを使用してタスクをキャンセルできます.タスクが待ち行列にある場合は、直接キャンセルされます.タスクがすでに実行されている場合、cancelのboolean値パラメータmayInterruptIfRunningは、タスクが実行を開始したときに、タスクを実行するスレッドを中断しようとする必要があるかどうかを示すために使用されます.(注意:これは、タスクが割り込みを受信できるか否かを示すものであり、タスクが割り込みを検出して処理できるか否かを示すものではない)だから、自分のタスクで割り込みフラグビットを判定することによってタスクを終了しなければならない.4.臨界リソースにアクセスする場合は、スレッド同期メカニズムを使用します.synchronizedキーワードはメソッドを修飾したり、コードブロックを修飾したりすることができますが、コンストラクタ、プロパティなどを修飾することはできません.
 
5.スレッドブロック割込みの処理:
スレッドスケジューリングに関連するブロック呼び出し(wait、sleep、joinなど)を実行すると、割り込みが発生すると、ブロックされたスレッドは「できるだけ速く」InterruptedExceptionから放出されます.したがって、try{//wait、sleepまたはjoin}catch(InterruptedException){//一部の割り込み処理作業}を次のコードフレームワークで処理できます.
<1>.sleep()静的メソッドは、指定した時間内に現在実行中のスレッドを一時停止させますが、ロックフラグは解放されません.現在のスレッドをブロック状態にし、指定した時間内に実行しません.他のスレッドは、その間に実行機会を得ることができます.スレッドのスリープは自動的に蘇り、実行可能な状態に戻ります.実行状態ではありません.いつ実行するかは、スレッドプールがどのようにスケジューリングされるかによって決まります.
<2>.wait()メソッドは、他のスレッドがオブジェクトのnotifyメソッドまたはnotifyAllメソッドを呼び出す前に、現在のスレッドが待機します.スレッドは、ロックフラグを解放し、他のスレッドがロックをプリエンプトする機会を与えます.現在のスレッドには、現在のオブジェクトロックが必要です.現在のスレッドがこのロックの所有者でない場合、IllegalMonitorStateException例外が放出されます.現在のオブジェクトロックを起動する待機スレッドは、notifyまたはnotifyAllメソッドを使用し、同じオブジェクトロックを持つ必要があります.そうしないと、IllegalMonitorStateException例外も放出されます.wait()およびnotify()はsynchronized関数またはsynchronized blockで呼び出される必要があります.non-synchronized関数またはnon-synchronized blockで呼び出すとコンパイルは可能ですが、実行時にIllegalMonitorStateExceptionの異常が発生します.
<3>.yield静的メソッドは、現在実行中のスレッドオブジェクトを一時停止し、他のスレッドを実行します.yield()は、現在のスレッドを実行可能状態に戻すだけなので、yield()を実行するスレッドは、実行可能状態に入った直後に実行される可能性があります.yield()は、同じ優先度またはそれ以上の優先度のスレッドを実行する機会しかありません.
<4>.joinメソッドは、スレッドが追加されたスレッドが完了するまで、現在のスレッドの実行を停止することを保証します.ただし、追加したスレッドが生存していない場合は、現在のスレッドを停止する必要はありません.
t 1スレッドでt 2を呼び出す.join()は、t 2スレッドの実行が完了した後にt 1が実行される必要がある.