java——利用生產者消費者模式思想實現簡易版handler機制


參考教程:http://www.sohu.com/a/237792762_659256

首先說一下這里面涉及到的線程:

1.mainLooper:

  這個線程可以理解為消費者線程,里面運行了一個死循環,不斷進行消息出隊和處理消息這兩個動作。

2.workLooper:

  這個線程就相當於是生產者線程,進行消息入隊。

3.程序本身的線程:

  這個線程是隱式的,也就是我們運行程序的這個線程,知道有這樣一個東西的存在能幫助我們理解整個程序的運行。

 

然后介紹每一個類:

1.Message:

  這個類的作用是存儲一個生產者生產出來的具體的消息,就類似鏈表隊列中的一個節點,自行定義需要存儲的內容。

  code:消息要執行的具體動作代碼

  msg:消息內容

  target:用來關聯hadler,根本目的時為了使這幾個類共享一個MessageQueue,這個很重要

2.MessageQueue:

  這個類就是生產者和消費者線程需要共享的一個存儲消息的隊列,生產者將消息放入這個隊列,消費者取出消息並處理。

  內部實現是用了BlockingQueue,這個隊列特別的地方就是出隊和入隊的時候是阻塞的,也就是說當隊列中沒有元素的時候,出隊這個動作會引起線程阻塞,直到有元素入隊;同理入隊也會因為隊列滿而引起線程阻塞,直到有元素出隊。

  這個類中定義了兩個方法:next和enqueueMessage分別對應出對和入隊。  

  需要考慮的就是如何將生產者和消費者多線程共享這個隊列?這個在下面解釋。

3.Looper

  這個類是整個機制的核心,理解了這個類這個機制怎樣運行的也就基本清楚了。

  這個類主要是為mainThread服務的,類中定義了一個靜態常量ThreadLocal,用來保存某個線程的共享變量,在這里它存的是一個:Looper,也就是這個類本身的一個實例。

  mainThread這個線程需要在開始線程的時候通過Looper.prepareMainLooper()創建一個looper。然后調用loop(),這個函數就是mainThread的主循環,不斷地做兩件事:消息出隊和處理消息。

4.Handler:

  這個類主要定義了兩個方法:sendMessage()和handleMessage(),即發送消息和處理消息,其中sendMessage()就是將消息入隊,而handleMessage()設計成抽象方法,根據不同的實際情況設計不同的消息處理方法。

 

介紹完這四個類之后,要思考的就是生產者和消費者對MessageQueue的共享問題

  主程序開啟了mainThread進程,這個進程在一開始就創建了一個Looper和一個mq,由於一開始這個mq是一個空隊列,mainThread執行到loop()里的for循環時被阻塞在msg = me.mq.next();此時的程序並不會因此停止,而是向下執行,創建了一個handler,hadler中傳入了mainThread中創建的那個Looper,並將這個Looper中的mq和hadler中的mq相關聯,換句話說,此時handler中的mq就是mainThread中looper的mq。程序接着往下走,又創建了一個workThread,這個線程傳入message,這個message除了保存了自身的信息之外,還保存了剛剛創建的handler,把這樣的message傳入workThread執行入隊操作時,就能夠將這個message存入handler中的mq中,此時也就將生產者和消費者這兩個線程的MessageQueue指向了同一片內存。

 

  為什么要把MainThread創建的looper保存在ThreadLocal中?為什么要設置成static final?

    這個東西叫做線程本地變量,也有些地方叫做線程本地存儲,其實意思差不多。ThreadLocal為變量在每個線程中都創建了一個副本,那么每個線程可以訪問自己內部的副本變量。

    looper是在MainThread中創建的,也就是說每創建一個MainThead,這個MainThread中有就一個looper這樣一個變量,不同的MainThread中有不同的looper。

    設置成static final 這樣就保證Looper這個類在創建的時,ThreadLocal就已經聲明和創建。

 

思考的還是不太全面,先把代碼先貼在這里:

Message.java

package Handler_test;

public class Message {
    
    private int code;
    private String msg;
    Handler target;
    
    public Message() {
        
    }
    
    public Message(int code, String msg, Handler handler) {
        this.code = code;
        this.msg = msg;
        this.target = handler;
    }

    public  int getCode() {
        return code;
    }
    
    public void setCode(int code) {
        this.code = code;
    }
    
    public String getMsg() {
        return this.msg;
    }
    
    public void setMsg(String msg) {
        this.msg = msg;
    }
    
}

MessageQueue.java

package Handler_test;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MessageQueue implements IMessageQueue{

    private BlockingQueue<Message> queue;
    
    public MessageQueue(int cap) {
        
        this.queue = new LinkedBlockingQueue<>(cap);
    }

    @Override
    public Message next() throws InterruptedException {
        // TODO Auto-generated method stub
        return queue.take();
    }

    @Override
    public void enqueueMessage(Message msg) throws InterruptedException {
        // TODO Auto-generated method stub
        try {
            queue.put(msg);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Handler.java

package Handler_test;

public abstract class Handler {

    private MessageQueue mq;
    
    public Handler(Looper looper) {
        mq = looper.mq;
    }
    
    public Handler() {
        Looper.myLooper();
    }
    
    public void sendMessage(Message msg) {
        try {
            mq.enqueueMessage(msg);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

    public  abstract void handleMessage(Message msg);
    
}

Looper.java

package Handler_test;

public class Looper {

    MessageQueue mq;
    
    //用來保存某個線程的共享變量
    //為什么要做成常量?
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
    
    private static Looper sMainLooper;
    
    public Looper() {
        mq = new MessageQueue(2);
    }
    
    public static void prepare() {
        if(sThreadLocal.get() != null) {
            throw new RuntimeException("一個線程只能創建一個looper");
        }
        sThreadLocal.set(new Looper());
    }
    
    public static void prepareMainLooper() {
        prepare();
        //??直接寫Looper行么
        synchronized(Looper.class) {
            if(sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    public static Looper myLooper() {
        // TODO Auto-generated method stub
        return sThreadLocal.get();
    }
    
    public static Looper getMainLooper() {
        return sMainLooper;
    }
    
    public static void Loop() {
        final Looper me = myLooper();
        if(me == null) {
            throw new RuntimeException("No looper; Looper.prepared() wasn't called on this thread");
        }
        for(;;) {
            Message msg = null;
            try {
                msg = me.mq.next();
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            if(msg != null) {
                msg.target.handleMessage(msg);
            }
        }
    }
}

Main.java

package Handler_test;

import java.util.Random;

public class Main {
    
    public static void main(String[] args) {
        
        MainThread mainThread = new MainThread();
        //這里start()創建一個執行run()方法的新線程
        //調用start方法表示此進程處於 可運行狀態 ,但並不一定正在運行
        //線程的調度依賴系統提供的服務
        mainThread.start();
        //mainThread在准備looper時耗時,而程序的讀取不會因為線程的耗時而停止
        //但是之后的程序需要mainThread創建好looper后才能執行
        //我認為Thread.sleep(100);是表示這個程序的線程,
        //而不是mainThread以及workThread中的任何一個
        //當looper創建好后Looper.getMainLooper()判斷非空,跳出循環,程序向下執行
        //mainThread在創建完looper后由於mq中沒有消息而卡在了msg = me.mq.next();
        
        while(Looper.getMainLooper() == null) {
            try {
                Thread.sleep(100);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
        //這里傳入了mainThread中創建的looper
        //handler的構造函數中:mq = looper.mq;也將looper中的mq傳給了handler
        //這樣looper中的mq就和handler中的mq關聯起來了
        //也就是共用一片內存
        Handler handler = new Handler(Looper.getMainLooper()) {
            @Override 
            public void handleMessage(Message msg) {
                System.out.println("execute in: " + 
            Thread.currentThread().getName());
                switch(msg.getCode()) {
                case 0:
                    System.out.println("0 case");
                    break;
                case 1:
                    System.out.println("1 case");
                    break;
                case 2:
                    System.out.println("2 case");
                    break;
                default:
                    System.out.println("default");
                    break;
                }
            }
        };
        
        //將前面寫好的handler傳入msg中
        Message msg1 = new Message(0, "first", handler);
        WorkThread workThread1 = new WorkThread(handler, msg1);
        Message msg2 = new Message(2, "two", handler);
        WorkThread workThread2 = new WorkThread(handler, msg2);
        workThread1.start();
        workThread2.start();
        //workThread中的run方法執行入隊操作handler.sendMessage(message);
        //這時將msg放入了handler中定義的那個mq
        //這個mq同時也是looper中的那個mq
        //此時由於mq這個隊列不為空,mainThread被喚醒
        //繼續執行msg.target.handleMessage(msg);
        //由於loop()是一個死循環,mainThread在處理完一條msg之后會繼續取下一條msg
        //循環這個過程
    }
    
    //為什么要把這兩個線程做成內部類,放在外面不行么?
    public static class WorkThread extends Thread{
        
        private Handler handler;
        
        private Message message;
        
        public WorkThread(Handler handler, Message message) {
            setName("Work Thread");
            this.handler = handler;
            this.message = message;
        }
        
        @Override
        public void run() {
            //這句話可以不寫么?
            super.run();
            //模擬耗時
            Random random = new Random();
            try {
                Thread.sleep(random.nextInt(10)*30);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            //消息入隊
            handler.sendMessage(message);
        }
    }
    
    public static class MainThread extends Thread{
        public MainThread() {
            setName("MainThread");
        }
        
        @Override
        public void run() {
            super.run();
            Looper.prepareMainLooper();
            System.out.println(getName() + " the looper is prepared.");
            Looper.Loop();
        }
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM