1. 程式人生 > >Android訊息機制原理,仿寫Handler Looper原始碼跨執行緒通訊原理--之執行緒間通訊原理(一)

Android訊息機制原理,仿寫Handler Looper原始碼跨執行緒通訊原理--之執行緒間通訊原理(一)

前言:我們都知道Android的執行緒通訊是用Handler、Looper機制實現的,面試也經常問道,網上也有很多文章介紹原始碼但是可能很多小白只是機械是的記憶,回答不清楚原理究竟是怎麼回事。下邊我將一步一步仿寫一個Handler、Looper模擬Android的執行緒間通訊,很簡單一看就懂。

第一節 執行緒間通訊原理

       所謂的通訊無非就是把“你”和“我”的訊息傳達到對方,方式很多種發信息、打電話、寫信、說話、手勢、眼神、留言等等。我覺得多數人都是被這個"執行緒間通訊"給誤導了,我敢肯定每個人做開發過程中都實現過這個執行緒間通訊。比如大家熟悉的多執行緒併發中同步的問題,多執行緒訪問同一個變數需要加鎖,也就是說多執行緒可以訪問一個共有的變數

       利用多執行緒可以訪問同一個共有變數,我們下邊實現一個簡易的留言板要實現執行緒2先留言,執行緒1過一會檢視留言:

public class TestThreadMsg0 {
    static String message;//全域性公共變數

    static class Thread1 extends Thread{
        @Override
        public void run() {
            super.run();
            for (;;) {
                if (TestThreadMsg0.message != null){
                    System.out.println("執行緒:"+Thread.currentThread().getId()+"   收到:"+TestThreadMsg0.message);
                    TestThreadMsg0.message = null;
                    System.out.println("");
                    System.out.println("");
                }
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            super.run();
            for (;;) {
                try {
                    String message="來自執行緒"+Thread.currentThread().getId()+"的訊息";
                    System.out.println("執行緒:"+Thread.currentThread().getId()+"   傳送:"+message);
                    TestThreadMsg0.message = message;
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

        Thread2 t2 = new Thread2();
        t2.start();

    }
}

       程式碼很簡單不必多說,這樣就實現了一個簡單的留言板,也就是實現了執行緒2和執行緒1的通訊。這就是執行緒間通訊的基本原理是不是很簡單!看到這裡你肯定會想起來以前一定寫過類似的程式碼!

        這個執行緒通訊實現有兩個嚴重問題(1)不是即時通訊,執行緒1每隔一秒才看訊息不及時  (2)如果執行緒2發訊息過快會導致執行緒1還沒看就被覆蓋了會丟失訊息針對這兩個問題來個優化版本,採用訊息佇列緩衝訊息:

public class TestThreadMsg1 {
    //阻塞式訊息佇列
    static LinkedTransferQueue mQueue = new LinkedTransferQueue();

    static class Thread1 extends Thread{
        @Override
        public void run() {
            super.run();
            for (;;) {
                String message = null;
                try {
                    message = (String) mQueue.take();//訊息佇列取出最新訊息
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("執行緒:"+Thread.currentThread().getId()+"   收到:"+ message);
                System.out.println("");
                System.out.println("");
            }
        }
    }

    static class Thread2 extends Thread{
        @Override
        public void run() {
            super.run();
            for (;;) {
                try {
                    String message="來自執行緒"+Thread.currentThread().getId()+"的訊息";
                    System.out.println("執行緒:"+Thread.currentThread().getId()+"   傳送:"+message);
                    mQueue.put(message);//放到訊息佇列
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

        Thread2 t2 = new Thread2();
        t2.start();

    }
}

        上邊採用LinkedTransferQueue阻塞是佇列,實現訊息佇列。這樣執行緒2可以隨意發任性發訊息不會丟失,執行緒1用一個死迴圈不斷讀取可以做到一有訊息及時響應!

        第二個改進版本雖然實現了2個執行緒間的即時通訊,但是還有2個顯而易見的問題只有一個公共的全域性訊息佇列,如果再來幾個執行緒怎麼辦?(1)多個執行緒讀取這個公共的訊息佇列就涉及到了上邊提到的加鎖同步問題,加鎖就有等待意味不可能即時通訊。(2)還有一個問題就是所有執行緒的訊息都在一個公共的全域性訊息佇列每個執行緒都可能看到這不是執行緒安全的

        針對存在的2個問題,優化方案是每個執行緒都有一個訊息佇列,犧牲記憶體空間換時間這樣即安全由能即時通訊每個執行緒只遍歷自己的訊息佇列就好了。詳細程式碼請看下一節: