先来讲下观察者模式,其实在Android开发中,咱们使用观察者模式的时候仍是很是多的,不管是广播使用的发布-订阅模式,仍是listview的notifyDataSetChanged,或者是RxJava的使用,都是观察者模式的运用。今天就来从新看一下观察者模式。html
订阅模式,又称观察者模式。定义对象间一种一对多的依赖关系(注册),使得当一个对象改变状态后,则全部依赖它的对象都会获得通知并被自动更新(通知)。说白了就是个注册,通知的过程。java
先来定下一种场景,我每月都会在微信上,收到房东的房租单(跪舔有房的大佬们),假设房东是被观察者,我是观察者,租客我把联系方式留给房东这就是一个注册过程,每月月初,房东大佬都会通知咱们交租(被观察者变化通知观察者)。设计模式
JDK为咱们提供了Observer与Observable内置接口,咱们能够很方便的使用它们。安全
public class TheOppressed implements Observer {
public String name;
public TheOppressed(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name +" 收到要交租信息");
}
}
public class Exploiter extends Observable {
public void postChange(String content){
setChanged();
notifyObservers(content);
}
}
public static void main(String[] args) {
Exploiter exploiter = new Exploiter();
TheOppressed theOppressed1 = new TheOppressed("打工仔1");
TheOppressed theOppressed2 = new TheOppressed("打工仔2");
TheOppressed theOppressed3 = new TheOppressed("打工仔3");
exploiter.addObserver(theOppressed1);
exploiter.addObserver(theOppressed2);
exploiter.addObserver(theOppressed3);
exploiter.postChange("打工仔们快来交房租啦~~");
}
复制代码
其实每每观察者模式咱们本身也能够来写,而不用系统提供的方式微信
/** * 观察者 */
public interface MyObserver {
/** * 找我 * @param content 找我啥事 */
void callMe(String content);
}
/** * 被观察者 */
public interface MySubject {
/** * 观察者注册 */
void registerObserver(MyObserver observer);
/** * 删除观察者 */
void removeObserver(MyObserver observer);
/** * 主题有变化时通知观察者 */
void notifyObserver();
}
复制代码
先定义两个接口,实现了观察者模式的基本方法多线程
/** * 房东 */
public class Exploiter implements MySubject {
private String name;
//存放观察者的集合
private List<MyObserver> observers;
private String content;
public Exploiter(String name) {
this.name = name;
observers = new ArrayList<>();
content = "";
}
@Override
public void registerObserver(MyObserver observer) {
this.observers.add(observer);
}
@Override
public void removeObserver(MyObserver observer) {
int index = observers.indexOf(observer);
if (index > 0) {
observers.remove(observer);
}
}
@Override
public void notifyObserver() {
for (int i = 0; i < observers.size(); i++) {
//遍历全部房客,并调用他们的通知方法
MyObserver observer = observers.get(i);
observer.callMe(content);
}
}
/** * 房东要发信息啦 */
public void postMessage(String content) {
if (null != content && !content.trim().equals("")) {
this.content = content;
this.notifyObserver();
}
}
}
/** * 劳动人民 */
public class TheOppressed implements MyObserver {
public String name;
public TheOppressed(String name) {
this.name = name;
}
@Override
public void callMe(String content) {
System.out.println(name + " " + content);
}
}
复制代码
调用代码用下并发
public class Test {
public static void main(String[] args) {
Exploiter exploiter = new Exploiter("房东");
TheOppressed theOppressed1 = new TheOppressed("打工仔1");
TheOppressed theOppressed2 = new TheOppressed("打工仔2");
TheOppressed theOppressed3 = new TheOppressed("打工仔3");
exploiter.registerObserver(theOppressed1);
exploiter.registerObserver(theOppressed2);
exploiter.registerObserver(theOppressed3);
exploiter.postMessage("打工仔们快来交房租啦~~");
}
}
//结果
打工仔1 打工仔们快来交房租啦~~
打工仔2 打工仔们快来交房租啦~~
打工仔3 打工仔们快来交房租啦~~
复制代码
其实在JDK9以后,Observer 这些都已经被废弃了,主要由于它异步
其实咱们从上面很容易看出来,多观察者须要串行调用,被观察者发生动做,观察者要做出回应,若是观察霆太多,并且处理时间长怎么办?用异步,也许你会脱口而出,那么,异步的处理就要考虑到线程安全和队列的问题。ide
就是刚刚一样的场景,若是房东先发了一条涨租200,后来又发了一条,收房租(固然要多准备200),假设延迟性是很是的大的状况下,咱们不可能单线程串行一直等,太费性能了,开了多线程的状况下,那么就会出现问题,可能某人会先收到交房租,这样就乱了。函数
现实中有好多这种并发场景,一个或者多个线程,要等待另外一组线程执行完成后,才能继续执行的问题,jdk已的com.util.concurrent下为咱们提供了不少多线程的类,之后有机会再细讲,今天先用一个,CountDownLatch
。
CountDownLatch就一个线程同步工具,它至关于一个倒序计数器,用来协调多个线程的执行。多个线程经过调用它们所共享的计数器CountDownLatch的countDown方法来让计数器减1。经过await方法来阻塞当前线程,直到计数器变成0。达到线程阻塞直至其余线程执行完成被从新唤醒。主要有三个方法:
概念抄完,直接下手
先来定义一下Observer与Subject的接口
/** * 被观察者 */
public interface MySubject {
/** * 观察者注册 */
void registerObserver(MyObserver observer);
/** * 删除观察者 */
void removeObserver(MyObserver observer);
/** * 主题有变化时通知观察者 */
void notifyObserver();
}
/** * 观察者 */
public interface MyObserver {
/** * 找我 * @param content 找我啥事 */
void callMe(String content);
/** * 观察者名字 */
String getName();
}
复制代码
而后能够实现租客与房东的实现类
/** * 房东 */
public class Exploiter implements MySubject {
private String name;
// private List<MyObserver> observers;
private ConcurrentMap<String, MyObserver> observers;
private String content;
public Exploiter(String name) {
this.name = name;
// observers = new ArrayList<>();
observers = new ConcurrentHashMap<>();
content = "";
}
@Override
public void registerObserver(MyObserver observer) {
// this.observers.add(observer);
this.observers.put(observer.getName(), observer);
}
@Override
public void removeObserver(MyObserver observer) {
observers.remove(observer.getName());
// int index = observers.indexOf(observer);
// if (index > 0) {
// observers.remove(observer);
// }
}
@Override
public void notifyObserver() {
try {
long beginTime = System.currentTimeMillis();
CountDownLatch latch = new CountDownLatch(observers.size());
int i = 0;
for (MyObserver observer : observers.values()) {
MessageSending messageSending = new MessageSending(latch, observer, content);
messageSending.start();
i++;
}
// for (int i = 0; i < observers.size(); i++) {
// MyObserver observer = observers.get(i);
// observer.callMe(content);
// }
latch.await();
long endTime = System.currentTimeMillis();
System.out.println(name + "消息发送完毕,耗时:" + (endTime - beginTime));
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** * 房东要发信息啦 */
public void postMessage(String content) {
if (null != content && !content.trim().equals("")) {
this.content = content;
System.out.println(name + "发布信息:" + content);
this.notifyObserver();
}
}
}
/** * 劳动人民 */
public class TheOppressed implements MyObserver {
public String name;
public TheOppressed(String name) {
this.name = name;
}
@Override
public void callMe(String content) {
System.out.println(name + "接收到通知:" + content);
}
@Override
public String getName() {
return name;
}
}
复制代码
最后在main方法调用以下:
public class Test {
public static void main(String[] args) {
Exploiter exploiter = new Exploiter("房东");
TheOppressed theOppressed1 = new TheOppressed("打工仔1");
TheOppressed theOppressed2 = new TheOppressed("打工仔2");
TheOppressed theOppressed3 = new TheOppressed("打工仔3");
exploiter.registerObserver(theOppressed1);
exploiter.registerObserver(theOppressed2);
exploiter.registerObserver(theOppressed3);
exploiter.postMessage("这个月房租加200!!!");
exploiter.postMessage("打工仔们快来交房租啦~~");
}
}
复制代码
能够看到输出结果为:
房东发布信息:这个月房租加200!!!
打工仔2接收到通知:这个月房租加200!!!
打工仔1接收到通知:这个月房租加200!!!
往打工仔1消息发送完毕
往打工仔2消息发送完毕
打工仔3接收到通知:这个月房租加200!!!
往打工仔3消息发送完毕
房东消息发送完毕,耗时:2004
房东发布信息:打工仔们快来交房租啦~~
打工仔1接收到通知:打工仔们快来交房租啦~~
往打工仔1消息发送完毕
打工仔2接收到通知:打工仔们快来交房租啦~~
往打工仔2消息发送完毕
打工仔3接收到通知:打工仔们快来交房租啦~~
往打工仔3消息发送完毕
房东消息发送完毕,耗时:2001
复制代码
能够看到,使用多线程往观察者发送信息,观察者均可以很迅速的接收到信息,这是并行的,可是又保证了被观察者的多个消息之间是有前后顺序的。
观察者模式主要是对象的解耦,将观察者与被观察者之间彻底隔离。jdk提供的默认观察者Observer/Observable在多线程下有安全性问题,须要本身手写,JDK9以后已经废弃了。
PS:今天看的比较简单,却也挺重要吧,之前历来没注意过关于观察者模式的 多线程安全性问题,也算是查漏补缺吧。
参考
《Android源码设计模式》
观察者模式(二)——多线程与CountDownLatch浅析
下面是个人公众号,欢迎你们关注我