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();
        }
    }
}
java——利用生产者消费者模式思想实现简易版handler机制的更多相关文章
- Java设计模式—生产者消费者模式(阻塞队列实现)
		生产者消费者模式是并发.多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据.这篇文章我们来看看什么是生产者消费者模式,这个问 ... 
- java多线程 生产者消费者模式
		package de.bvb; /** * 生产者消费者模式 * 通过 wait() 和 notify() 通信方法实现 * */ public class Test1 { public static ... 
- 关于java中生产者消费者模式的理解
		在说生产者消费者模式之前,我觉得有必要理解一下 Obj.wait(),与Obj.notify()方法.wait()方法是指在持有对象锁的线程调用此方法时,会释放对象锁,同时休眠本线程.notify() ... 
- java 实现生产者-消费者模式
		生产和消费者模式有很多种,现在介绍几种常见的方式 wait/notify实现生产和消费者模式 1.使用wait/notify实现生产和消费者模式: public class Depot { // 实际 ... 
- java实现生产者消费者模式
		生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将 ... 
- java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现
		java多线程15 :wait()和notify() 的生产者/消费者模式 在这一章已经实现了 wait/notify 生产消费模型 利用await()/signal()实现生产者和消费者模型 一样 ... 
- Java生产者消费者模式
		为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能 ... 
- java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】
		java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ... 
- Java 生产者消费者模式详细分析
		*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ... 
随机推荐
- 32-回文字符串(dp)
			http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=37 回文字符串 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描 ... 
- 1020C Elections
			传送门 题目大意 现在有 n个人,m个党派,第i个人开始想把票投给党派pi,而如果想让他改变他的想法需要花费ci元.你现在是党派1,问你最少花多少钱使得你的党派得票数大于其它任意党派. 分析 我们枚举 ... 
- Luogu 1081 [NOIP2012] 开车旅行
			感谢$LOJ$的数据让我调掉此题. 这道题的难点真的是预处理啊…… 首先我们预处理出小$A$和小$B$在每一个城市的时候会走向哪一个城市$ga_i$和$gb_i$,我们有链表和平衡树可以解决这个问题( ... 
- rest-framework组件 之 分页
			分页 简单分页 from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class PNPag ... 
- 完整读写txt 并提取{}里的内容
			using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Lin ... 
- 选择性搜索(Selective Search)
			1 概述 本文牵涉的概念是候选区域(Region Proposal ),用于物体检测算法的输入.无论是机器学习算法还是深度学习算法,候选区域都有用武之地. 2 物体检测和物体识别 物体识别是要分辨出图 ... 
- JDK Linux下安装
			下载jdk-6u45-linux-x64.bin到/root目录下执行./jdk-6u45-linux-x64.bin 会在/root目录下生成 jdk1.6.0_45 文件 之后配置环境变量 编辑/ ... 
- 小程序开发笔记【二】,抽奖结果json数据拼装bug解决
			抽奖结果数据json格式数据拼接bug,如下图,只发布了两个奖项,每个奖项设置2个奖品,但最后拼接数据的时候出现3个奖项 json数据格式如下 "luckyResult":[ { ... 
- [SinGuLaRiTy] 贪心题目复习
			[SinGuLaRiTy-1024] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. [POJ 2709] 颜料 (Painter) 题目描述 ... 
- loj #2007. 「SCOI2015」国旗计划
			#2007. 「SCOI2015」国旗计划 题目描述 A 国正在开展一项伟大的计划 —— 国旗计划.这项计划的内容是边防战士手举国旗环绕边境线奔袭一圈.这项计划需要多名边防战士以接力的形式共同完成 ... 
