Android中的執行緒通訊

❀卜卜ღ?Bruce發表於2019-03-12

執行緒

android中的執行緒是執行Runnable介面的Thread

程式碼如下

new Thread(new Runnable() {
            @Override
            public void run() {
                //設定執行緒優先順序,減少與UI執行緒的競爭
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
            }
        }).start();
複製程式碼

程式碼執行在Runnable.run()裡面。通過android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)可以設定執行緒的優先順序別,減少執行緒與UI執行緒的競爭。

[除此以外android還提供了更好的選擇HandlerThread, AsyncTask, 和IntentService.]

執行緒池

ThreadPoolExecutor能管理程式,開發者只需要把執行緒加入執行緒佇列即可。 建立執行緒池的時候需要舒適化一些引數,見下面程式碼,摘抄自阿里巴巴的ANDROID開發手冊

        //獲取可用cpu核數量
        int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
        //執行完後的存活時間
        int KEEP_ALIVE_TIME = 1;
        //執行完後的存活時間的單位
        TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
        //存放執行緒的佇列
        BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
        ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
                NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, taskQueue,
                new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
        //執行任務
        executorService.execute(new Runnable() {
            @Override
            public void run() {

            }
        });
複製程式碼
    //建立後臺執行緒
    private class BackgroundThreadFactory implements ThreadFactory {
        @Override
        public Thread newThread(@NonNull final Runnable r) {
            Runnable wrapperRunnable = new Runnable() {
                @Override
                public void run() {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    r.run();
                }
            };
            return new Thread(wrapperRunnable);
        }
    }
    /*拒絕策略,當佇列滿後的處理方式,系統提供了
     *AbortPolicy
     *DiscardPolicy
     *DiscardOldestPolicy
     *CallerRunsPolicy
     */
    private class DefaultRejectedExecutionHandler implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

        }
    }
複製程式碼

##執行緒通訊 執行緒間通訊分為兩種,一種是和主線通訊,一種是後臺執行緒之間通訊。 當與主執行緒通訊的時候,是不可以阻塞主執行緒的,所以一般使用handler來通訊,先建立一個在UI執行緒的Handler

    Handler handler = new UiHandler();

    private static class UiHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    //更新UI等操作
                    break;
                default:
                    break;
            }
        }
 
複製程式碼

然後執行完耗時操作之後發訊息給主執行緒去執行UI操作

        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // 耗時操作
                handler.obtainMessage(1).sendToTarget();
            }
        });
複製程式碼

後臺執行緒之間的通訊主要涉及到同步與互斥。 如果在不同執行緒中操作同一資料,可能造成資料讀寫狀態不同步,那麼我們就需要在每一次的讀寫中鎖住這個值。 如下兩個執行緒。

      class RunableA implements Runnable{
        @Override
        public void run() {
            synchronized (MainActivity.class){
                Log.d("hah","A start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("hah","A end");
            }
        }
    }
    class RunableB implements Runnable{
        @Override
        public void run() {
            synchronized (MainActivity.class){
                Log.d("hah","b start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("hah","b END");
            }
        }
    }
複製程式碼
        executorService.execute(new RunableA());
        executorService.execute(new RunableB());
複製程式碼

列印如下:

14:56:53.083 3382-3395/activity.activitytest D/bruce: A start

14:56:55.083 3382-3395/activity.activitytest D/bruce: A end

14:56:55.153 3382-3396/activity.activitytest D/bruce: b start

14:56:57.153 3382-3396/activity.activitytest D/bruce: b END

可見B 是等 A 釋放之後才執行的。

再來看看wait和notify的應用。

    class RunableA implements Runnable{
        @Override
        public void run() {
            synchronized (MainActivity.class) {
                Log.d("bruce", "A wait");
                try {
                    MainActivity.class.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                Log.d("bruce", "A start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("bruce", "A end");
            }
        }
    }
    class RunableB implements Runnable{
        @Override
        public void run() {
            synchronized (MainActivity.class) {
                Log.d("bruce", "b start");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("bruce", "b END");
                MainActivity.class.notify();
            }
        }
    }
複製程式碼

執行緒A 開始的時候先wait,然後線上程B結束的時候notify 列印如下:

15:17:34.423 4365-4378/? D/bruce: A wait

15:17:34.423 4365-4379/? D/bruce: b start

15:17:36.423 4365-4379/activity.activitytest D/bruce: b END

15:17:36.423 4365-4378/activity.activitytest D/bruce: A start

15:17:38.433 4365-4378/activity.activitytest D/bruce: A end

會發現先執行A然後等待,接著開始制定B 等B notify之後又開始執行A;

以上就是執行緒的簡單應用。

相關文章