スレッドの基礎をAndroidの

マルチスレッド開発はAndroidの開発では非常に一般的で、マルチスレッド関連の問題は、私たちは、Androidでマルチスレッドについて話しなければならないので、今日、質問をインタビューします開発者でもあります。
以下のようなポイントは次のとおりです。

  • スレッドの概要
  • Javaでのスレッド
  • Androidのスレッド
    • AsyncTask
    • HandlerThread
    • IntentService
  • スレッドプール
    • スレッドプールの利点
    • スレッドプールの使用状況
    • プールの例スレッド
    • 一般的な4つのスレッドプール
  • 概要

スレッドの概要

スレッドのスケジューリングは、最小単位のCPUです。スレッドもありますが限られたシステムリソース。そのスレッドは無制限で生産することができないと、スレッドの作成と破棄は、いくつかのオーバーヘッドを持っています。

だから、どのようにオーバーヘッドによってもたらさ頻繁にスレッドの作成と破壊を回避するには?
スレッドプールの使用は、スレッドプールは、それによってスレッドを再利用し、管理することで、システムの安定性を高め、スレッドの一定数をキャッシュします。

二つのカテゴリーでのAndroidのスレッド:

メインスレッド:関連する論理インタフェースの相互作用を処理するための、典型的には唯一つのアプリケーション。
子スレッド:メインスレッドの子スレッドのほかには、主に、時間のかかる操作を実行するためにANRによって引き起こされる目詰まりを防ぐためにメインスレッドを使用しています。

Javaでのスレッド

参照:スレッドのJavaの基盤を

スレッドの形でのAndroid

独自のJavaスレッドクラスに加えて、Androidはまた、マルチスレッドのAndroidの開発を容易にするため、非同期型のカプセル化します。

AsyncTask

AsyncTask Androidは良いパッケージは、軽量非同期型であり実装サブクラスが、すなわち使用を継承するために、AsyncTask自体は、抽象クラスです。私はAsyncTaskパッケージ自体は非常に使いやすい、非常に簡単です、私たちは多かれ少なかれ通常の開発を使用しているべきだと考えています。

AsyncTaskは抽象ジェネリッククラスです。
public abstract class AsyncTask<Params, Progress, Result>
前記パラメータの以下の3つの汎用タイプの意味:
PARAMS:受信非同期タスクのパラメータの型の初めに、
進捗状況:プロセス非同期タスク、ダウンロードの進行状況値の戻り値の型;
結果:非同期タスクが完了した後、リターン結果の型。
決定AsyncTaskの場合は特定のパラメータを渡す必要があり、その後、3つの汎用パラメータが代わりにボイドの使用することができます

AsyncTaskのサブクラス実装:

public class MyTask extends AsyncTask<String, Integer, Integer>  {

    //执行线程任务前的操作,可以用于初始化参数
    @Override
    protected void onPreExecute() {
        super.onPreExecute();

    }

    //在子线程中运行,用于处理耗时任务
    //这里随便写了个更新进度的逻辑
    @Override
    protected Integer doInBackground(String... strings) {
        //每0.1秒更新一次进度,直到100
        for(int i = 0;i<100;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            publishProgress(i);
        }
        return null;
    }

  //主线程中进行,利用参数中的数值就可以对界面元素进行相应的更新,
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
    }

    //可以利用返回的数据来进行一些UI操作,在主线程中进行
    @Override
    protected void onPostExecute(Integer integer) {
        super.onPostExecute(integer);
    }

    
}

上記の4つの方法の中核方法AsyncTaskです。

onPreExecute() このメソッドはバックグラウンドタスクを呼び出す前には、メインスレッドで実行、実行を開始しますこのようなのような進捗ダイアログを表示するなどのパラメータや、画面上のいくつかの初期の作品を、初期化するために使用することができます。

doInBackground(文字列...文字列は):この方法ではすべてのコードがしますここでは、サブスレッドで実行我々は時間のかかる作業を行う場所です
タスクが完了すると実行されるタスクの結果できAsyncTask第三の一般的なパラメータがvoidを指定した場合、タスクの実行結果に戻ることはできません、return文によって返されます。サブスレッドであるので、このメソッドは、UI操作ではありません。

onProgressUpdate(整数値...) この方法では、メインスレッドでは、UI上で動作することができ、doInBackground()メソッド素早く呼び出した後publishProgress(整数値...)方法、呼び出され、この方法は、それに応じて更新された要素をインターフェースすることができる数値パラメータを使用して、バックグラウンドタスクに渡されたパラメータを搬送されます。

onPostExecute(整数整数):doInBackground()メソッドが完了すると戻り、このメソッドはすぐにreturn文経由で呼び出されます。返された戻りデータは、このメソッドにパラメータとして渡され、データは、いくつかのUIの操作を返すために利用することができるメインスレッドでそのようなタスクの実行の結果を示すように、。

シーケンスを呼び出す上記の方法のいくつか:onPreExecute() - > doInBackground() - > publishProgress() - > onProgressUpdate() - > onPostExecute()
あなたは進捗情報を更新しない場合はでした:onPreExecute() - > doInBackground( ) - > onPostExecute()。

上記の4つの方法の別の添加は、AsyncTaskも提供onCancelled()もれる方法、メインスレッドで実行されない)方法は、タスクをキャンセルする場合、onCancelled()は(キャンセルAsyncTaskと呼ばれる)、この時間onPostExecuteを(呼ばれ、それは、ことに留意すべきであるAsyncTaskは、()メソッドは、実際にタスクをキャンセルするつもりはない、キャンセル、ちょうど状態を解除するタスクを設定し、我々はタスクを終了決定するためにdoInBackground()に必要ですあなたは通話割り込みスレッドを終了したいと同じように()メソッド、ちょうど壊れたとして、内部スレッドを決定し、スレッドを中断するようにマークする必要がマークすること。

HandlerThread

AndroidのHandlerThreadは、パッケージの良い軽量な非同期タイプです。実際には、名前から推測することは困難ではない本質は新しいスレッドを作成しますし、他のスレッドを使用する方が便利になると通信Threadクラスの継承とカプセル化ハンドラクラスを使用してHandlerThreadされますHandlerThreadは、主に複数のスレッドを開く必要なしに、時間のかかる操作の数を実行するために使用されます。

;迅速ルーパーオブジェクトを使用して新しいワーカースレッドを作成することにより、Threadクラスの継承
Handlerクラスをカプセル化することによって、すぐに他のスレッドと通信ハンドラを作成します。

次のようにHandlerThreadを使用しました:

        //1.创建HandlerThread的实例对象,传入的参数为线程的名字
        HandlerThread mhandlerThread = new HandlerThread("MyHandlerThread");
        //2.启动HandlerThread线程
        mhandlerThread.start();
        //3.将HandlerThread与Handler绑定在一起
        Handler workHandler = new Handler(mhandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                //这里会在子线程中执行
                super.handleMessage(msg);
            }
        };

メッセージと通常の送信方法には違いハンドラを行うものではありません。

        workHandler.sendMessage(msg);

mHandlerThread.quit()メソッドを使用して糸の端部は、スレッドのメッセージループを停止させます。

IntentService

4つのコンポーネント・サービスのいずれかを継承し、ラッパークラス内アンドロイド。
参照:サービスのAndroidの基礎を

スレッドプール

開発では、我々はサブスレッド動作終了後ハンドラを介して実行されているメインスレッドに切り替えられるまで、新しいスレッドが子スレッドをオンにする傾向があります。だから我々は、我々が作成した子スレッドを管理し、かつ無制限のサブスレッドを作成することができないので、彼らが原因あまりにも多くのリソースを占有し、クラッシュしたり、OOMにつながるに、お互いに最も可能性が高い競います。そのため、Javaは、我々が作成したスレッドプールのスレッドを管理するためにを提供してくれます。

スレッドプールの利点:

  1. 減少し、消費に起因する既存のスレッド、スレッドの作成と破棄を再利用することで、システムリソースの消費を削減します。
  2. より高速なシステム応答性、タスクが到着したときに、新しいスレッドを作成するために待つことなく、すぐに実行することができるようになります。
  3. 制御の同時スレッドの便利な数が無制限のスレッドが作成されている場合、追加のシステムリソースを大量に消費するだけでなく、それによってシステムの安定性を減らすこと、システムリソースや障害物のOOM条件が多すぎるを取ります。スレッドプールのスレッドは、効果的にリソースの使用を提供する、一様分布、チューニングを制御することができます。
  4. より強力な、スレッドプールは、規則的な周期と、スレッドプールのスレッド機能の制御可能数、シンプルで使いやすい提供しています。

スレッドプールを使用します

実際には、スレッドプールを使用すると、3つのステップに分けることができます。

  1. スレッドプールの例
  2. タスクを追加します
  3. スレッドプールを閉じます

スレッドプールの例

コンストラクタのスレッドプールを次のように

    public ThreadPoolExecutor(int corePoolSize,
              int maximumPoolSize,
              long keepAliveTime,
              TimeUnit unit,
              BlockingQueue<Runnable> workQueue,
              ThreadFactory threadFactory,
              RejectedExecutionHandler handler) 

corePoolSizeスレッドプール内のスレッドのコア数、デフォルトでは、スレッドプール内のコアスレッドは、スレッドプール内にアイドル状態であっても、生き残りました我々 allowCoreThreadTimeOutプロパティのThreadPoolExecutorない限り真の時間に、新しいタスク、keepAliveTimeがで指定されたタイムアウト時間の到着を待っている間にそこにアイドル状態のスレッドタイムアウトポリシーの中心にあるこの時間。タイムアウトが設定されたら、アイドル状態のコアのスレッドよりは終了します。
maximumPoolSizeスレッドプールに含まれるスレッドの最大数スレッドアクティビティは、この値に達した後、新たな雇用のフォローアップがブロックされる場合は、。これは、含まれているスレッド+スレッドの非コア数のコア数を
keepAliveTimeが非中核スレッドのタイムアウト期間はアイドルである、それが回収されます。この時間以上アイドル非中核スレッド、ため、。allowCoreThreadTimeOutプロパティがtrueに設定されている場合、この時間は、同じ実効コア糸を製造します。
単位時間単位のパラメータがkeepAliveTimeがを指定しますこれは、列挙オブジェクトである
ワークキュー:実行待ちキューをブロックするスレッドプールのタスクを保存します。Runableオブジェクトはキューにスレッドプールから提出されたメソッドを実行する保存されています。

キュー 機能
ArrayBlockingQueue キュー注文要素FIFO(先入れ先出し)原理的にキューは、キューアレイベースの実装をブロック境界。
LinkedBlockingQueue キューの注文要素FIFO(先入れ先出し)原則として、キューのブロックリストの実装に基づきます。
SynchronousQueue キューを遮断するのは内部容量がありません。その内には、バッファスペースがありません。私たちは離れて取るしようとしているときに、データのための要素にのみ可能SynchronousQueue。
PriorityBlockingQueue 優先無制限のブロッキングキューに。
BlockingQueueのインタフェースを実現 カスタムキューを遮断します。

threadFactoryスレッドファクトリは、スレッドプールのための新しいスレッドを作成して提供しています。ThreadFactoryのみnewThread方式でインターフェースです。デフォルトはDefaultThreadFactoryクラスです。
ハンドラは:オブジェクトがインタフェースを実装のRejectedExecutionHandler、インターフェースは一つだけrejectedExecution法があります。タスクキューは、スレッドプール内の完全かつアクティブなスレッドが定義された最大に達したか、成功したミッションを実行することができませんしているされている場合、この時間はでrejectedExecutionメソッドのRejectedExecutionHandler ThreadPoolExecutorを呼び出します4つの内部のRejectedExecutionHandlerクラスがインタフェースThreadPoolExecutorを実装してあります。デフォルトのスレッドプールでは、それが新しいタスクを処理できない場合AbortPolicy、RejectedExecutionExceptionが例外をスローです。

タスクを追加します

建設ThreadPoolExecutorオブジェクトが完了した後、あなたは、スレッドプールにタスクを追加することができます。
タスクを追加するには、2つの方法があります実行して提出するには、

最も単純なことである、()メソッドを実行するだけで、Runnableインタフェースを実装する必要がある行です。私たちは、タスクを実行するために提出を使用する場合でも、原因値を返さないメソッドを実行するので、私たちは、タスクが正常にプールスレッドで実行されるかどうかを確認することができません

この方法は、もう少し複雑です()を提出我々はタスクを送信するために提出し、それが未来を返します使用するとき、私たちは、このタスクによって、将来を判断することができるようになりますが正常に実行され、あなたがすることができ、将来のgetメソッドから返された値を取得します子スレッドは、タスクを完了したタスクが完了するまで、この方法はブロックします取得するには、(長いタイムアウト、TimeUnitで単位)を取得する使用方法は、時間のブロッキング期間後すぐに戻りますしなかった場合は、この時間は、タスクを実行すると、完了していなかった可能性です。私たちは、タスクが完了したかどうかを判断するためにFuture.isDone()メソッドを使用することができ、戻り値を取った後、そして、そして。

        //Future<?> submit(Runnable task)
        Future<?> future = threadpool.submit(new Runnable() {
            
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
                
            }
        });
        //Future<T> submit(Callable<T> task)
        Future<Integer> future2 = threadpool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception{
                Thread.sleep(3000);
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
                System.out.println("future2 任务结束");
                return 315;
            }
        });

スレッドプールを閉じます

あなたは、スレッドプール閉鎖するスレッドプールのシャットダウン()またはshutdownNowの()メソッドを呼び出すことができます
シャットダウン原理:SHUTDOWN状態にスレッドプールの状態を、そしてすべてのスレッドミッションを中断されていません。
shutdownNowの原理:スレッドプールの状態を停止するように設定され、その後、(実行中を含む)のスレッドを任意のタスクを中断し、実行を待っているタスクのリストを返します。
通常の状況下では、それはシャットダウン()スレッドプールが閉鎖されて呼び出すことが推奨され、タスクは必ずしも実行されない場合は、shutdownNowのと呼ばれています()。

例スレッドプール:

                //打印主线程id
        System.out.println("主线程id : " + Thread.currentThread().getId());//输出主线程id
        //实例化线程池
        ExecutorService threadpool = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        //构建任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
            }
        };
        //提交任务
        //方式1,execute
        threadpool.execute(task);
        //方式2,submit
                //Runnable任务
        Future<?> future = threadpool.submit(task);
                //Callable任务
        Future<Integer> future2 = threadpool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception{
                Thread.sleep(5000);
                System.out.println(Thread.currentThread().getId());//输出当前线程id
                System.out.println("future2 任务结束");
                return 315;
            }
        });

        while(!future2.isDone()) {
            System.out.println("任务future2还未执行完毕");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("任务future2执行完毕");
        try {
                        //获取返回值
            System.out.println("任务返回值为" + future2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //关闭线程池
        threadpool.shutdown();

次のように上記のコードの結果です。

主线程id : 1
子线程id :11
子线程id :12
任务future2还未执行完毕
任务future2还未执行完毕
任务future2还未执行完毕
13
future2 任务结束
任务future2执行完毕
任务返回值为315

一般的な4つのスレッドプール

設定パラメータの種類によっては、Javaは、最も一般的なスレッドプールは、4つのカテゴリがあります:

  1. 固定長スレッドプール(FixedThreadPool)
  2. タイミングスレッドプール(ScheduledThreadPool)
  3. スレッドプールをキャッシュすることができる(CachedThreadPool)
  4. シングルスレッドのスレッドプール(SingleThreadExecutor)

彼らは、直接または間接的な構成ThreadPoolExecutorそれぞれの機能を実現します。することにより、これら4つのスレッドプールエグゼキュークラス取得。

FixedThreadPool

固定長スレッドプールのみコアスレッド、スレッドの固定数、タスクキューサイズ制限なし(スレッドのタスクを超えてキュー内で待機します)。()Executors.newFixedThreadPoolによって作成されます

        //实例化定长线程池,参数为核心线程数量
        ExecutorService myFixedThreadPool = Executors.newFixedThreadPool(5);//设置线程数量固定为5
        //构建任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
            }
        };
        //提交任务
        myFixedThreadPool.execute(task);
        //关闭线程池
        myFixedThreadPool.shutdown();

newFixedThreadPoolのみカーネルスレッド、およびこれらのスレッドを回復することはないので、それはより迅速に、外部要求に応答することができます

ScheduledThreadPool

スレッドプールタイミングカーネルスレッドの固定数、Executors.newScheduledThreadPool(によって作成された(アイドル直ちに回収)非コアのスレッドを無制限に、)

        //实例化定时线程池,参数为核心线程数量
        ScheduledExecutorService myScheduledThreadPool = Executors.newScheduledThreadPool(5);//核心线程数为5
        //构建任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
            }
        };
        //用schedule提交任务,参数为任务,延迟时间,时间单位
        //eg: 延迟1s后执行
        myScheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS);
         //用scheduleAtFixedRate提交任务,参数为任务,延迟时间,间隔时间,时间单位
        //eg:延迟10ms后、每隔1000ms执行一次任务
        myScheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);

        //关闭线程池
        myScheduledThreadPool.shutdown();

ScheduledThreadPoolは、以下の方法を使用してタスクを実行します。

  1. schedule(Runnable command, long delay, TimeUnit unit):一定時間遅延の後に実行するためのRunnableタスク。
  2. schedule(Callable callable, long delay, TimeUnit unit):呼び出し可能なタスクは、一定の時間遅延の後に実行します。
  3. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):一定の時間遅延の後、周波数インターバルの期間は、定期的にタスクを実行します。
  4. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit):そしてscheduleAtFixedRate()メソッドは、非常に似ていますが、違いがあるscheduleWithFixedDelay()メソッドのサイクル間隔は、複数のタスクの実行には、次のタスクが開始距離に終了している、とscheduleAtFixedRate()メソッドサイクル間隔は、複数のタスクの開始であります間隔が開始し、次のタスクを実行する、すなわち、トリガ時間scheduleAtFixedRate方法はタスクが予測可能で行う一方、実行のタスクscheduleWithFixedDelay法の開始時間は、タスクの前に実行時間予測不可能であり、そしてありますこれは、固定されています。

CachedThreadPool

キャッシュ可能なスレッドプールコアスレッドカウントは、スレッドがアイドル状態のときの60秒以上がリサイクルされ、タイムアウト機構と、スレッドプールInteger.MAX_VALUEの内のスレッドの最大数0でプール内のスレッドがアクティブな場合スレッドプールは、タスクを処理する新しいスレッドを作成したとき。全体のスレッドプールのスレッドが60秒以上後にアイドル状態にある場合は、スレッドプールは、任意のスレッドを存在していないので、この時間は、それはほとんどすべてのシステムリソースを占有しません。

        //实例化定时线程池
        ExecutorService myCachedThreadPool = Executors.newCachedThreadPool();
        //构建任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
            }
        };
        //提交任务
        myCachedThreadPool.execute(task);
        //关闭线程池
        myCachedThreadPool.shutdown();

使用することは非常に簡単で、あなたが直接タスクを追加することができ、構成パラメータを必要としません。

SingleThreadExecutor

このスレッドプール内のシングルスレッドスレッドプールは、コアのみのスレッドである意味タスクキューのサイズに制限はありません、タスクがアクティブである場合、タスクキュー内の他のタスクを実行するためにキューに入れられますが、そのための治療を必要としませんスレッドの同期の問題

        ExecutorService mySingleThreadExecutor = Executors.newSingleThreadExecutor();
//构建任务
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程id :" + Thread.currentThread().getId());//输出当前线程id
            }
        };
        //提交任务
        mySingleThreadExecutor.execute(task);
        //关闭线程池
        mySingleThreadExecutor.shutdown();

シングルスレッドのスレッドプールは、同時IOには適していませんが、閉塞性応答を引き起こし、そのようなので、上のデータベース操作、ファイル操作として、UIスレッドの動作に影響を与える可能性があります。

概要

目的のスレッドが2つのインタフェースのうち主要な成果である執行機能、Runnableを基本的に使用され、呼び出し可能は難しいことではありません、しかし、原理を理解するために、我々はまた、より詳細なソースを学ぶ必要があります。そこ内容に関する多くのスレッドがありますが、これは、時間がより詳細な要約になり、ラフ要約されています。

おすすめ

転載: blog.csdn.net/weixin_33716557/article/details/90772386