多线程编程-设计模式之不可变对象模式

Immutable Object设计模式
适用场景:
1.被建模对象的状态变化不频繁:设置一个专门的线程用于被建模对象状态发生变化时创建新的不可变对象。而其他线程只是读取不可变对象的状态。此场景下一个小技巧就是Manipulator对不可变对象的引用使用volatile关键字进行修饰,既可以避免使用显示锁比如synchronize,又可以保证多线程间的内存可见性。
2.同时对一组相关的数据进行写操作,因此需要保证原子性
此场景下为了保证操作的原子性,通常的做法是使用显示锁,但若采用Immutable Object模式,将这一组相关的数据组合成一个不可变对象,则对这一组数据的操作就可以无需加显示锁也能保证原子性,这样既简化了编程,又提高了代码运行效率。
3.使用某个对象作为hashmap的key:一个对象作为HashMap的key被放入到HashMap中,若该对象的状态发生变化导致Hashcode发生变化,则会导致后面同样1的对象作为key去get的时候无法获取到关联的值,尽管改hashMap中的确存在已该对象作为key的条目。相反,由于不可变对象的状态不变,因此其HashCode也不变,这使得不可变对象非常适用于作为HashMap的key。

设计思路:

代码设计:手机号前缀与彩信中心之间的路由表关系  路由表可能发生变化  但是变化的频率并不高,并且不希望对这些数据的访问进行加锁等并发访问控制,以免产生不必要的开销和问题。

使用不可变对象维护路由表

package com.immutableobject.design;

import java.util.Collections;import java.util.HashMap;import java.util.Map;

/** * 彩信中心路由规则管理器 * * @author zhouhy * @create 2017 -09-29 上午11:03 */

public final class MMSCRouter {

//用volatile修饰保证多线程环境下该变量的可见性    private static volatile MMSCRouter instance = new MMSCRouter();

private final Map<String,MMSCInfo> routeMap;

public MMSCRouter(){        //把数据库中的数据加载到内存中 存为map        this.routeMap = MMSCRouter.retrieveRouteMapFromDB();    }

private static Map<String,MMSCInfo> retrieveRouteMapFromDB(){

Map<String,MMSCInfo> map = new HashMap<String,MMSCInfo>();        //从数据库中查出数据并存到map里        return map;    }

public static MMSCRouter getInstance(){

return instance;    }

/**     *     * 根据手机号前缀获取对象的彩信中心信息     * @param msisdnPrefix     * @return     */    public MMSCInfo getMMSC(String msisdnPrefix){

return routeMap.get(msisdnPrefix);

}

public static void setInstance(MMSCRouter newInstance){

instance = newInstance;    }

private static Map<String,MMSCInfo> deepCopy(Map<String,MMSCInfo> m){

Map<String,MMSCInfo> result = new HashMap<>();

for (String key:m.keySet()){

result.put(key,new MMSCInfo(m.get(key)));        }

return result;    }//会发生变化的数据要进行防御性复制    public Map<String,MMSCInfo> getRouteMap(){        //防御性复制        return Collections.unmodifiableMap(deepCopy(routeMap));    }}

使用不可变对象表示彩信中心数据:

package com.immutableobject.design;

/** * 使用不可变对象表示彩信中心信息 * * @author zhouhy * @create 2017 -09-29 上午11:08 */

public final class MMSCInfo {

/**     * 设备编号     */    private final String deviceId;

/**     * 彩信中心url     */    private final String url;

/**     * 该彩信中心所允许的最大附件大小     */    private final int maxAttachmentSizeInBytes;

public MMSCInfo(String deviceId,String url,int maxAttachmentSizeInBytes) {

this.deviceId = deviceId;        this.url = url;        this.maxAttachmentSizeInBytes = maxAttachmentSizeInBytes;    }

public MMSCInfo(MMSCInfo prototype){        this.deviceId = prototype.deviceId;        this.url = prototype.url;        this.maxAttachmentSizeInBytes = prototype.maxAttachmentSizeInBytes;

}    public String getDeviceId() {        return deviceId;    }

public String getUrl() {        return url;    }

public int getMaxAttachmentSizeInBytes() {        return maxAttachmentSizeInBytes;    }

}

参与者实例监控路由表数据变化:
package com.immutableobject.design;

/** * 与运维中心对接的类;Manipulator * * @author zhouhy * @create 2017 -09-29 上午11:23 */

public class OMCAgent extends Thread {

@Override    public void run() {        boolean isTableModificationMsg = false;        String updateTableName = null;        while (true){            /**             * 从与OMC连接的socket中读取消息并解析             * 解析到路由表更新消息后,重置MMSCInfo实例             */            if (isTableModificationMsg){

if ("MMSCInfo".equals(updateTableName)){                    MMSCRouter.setInstance(new MMSCRouter());                }            }        }    }}

使用注意问题:
1.被建模对象的状态变更比较频繁:
频繁创建不可变对象,增加垃圾回收。
2.防御性复制:如果不可变对象本身包含一些状态需要对该暴露,而相应的字段本身又是可变的(比如HashMap),那么返回这些字段方法的时候需要做防御性复制,以避免外部代码修改其内部状态。

CopyOnWriteArrayList:遍历时为线程安全的 应用了不可变对象模式

public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//复制原数组 并在此基础上将数组的最后一个元素设置为要添加的元素
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

时间: 2024-08-26 14:41:32

多线程编程-设计模式之不可变对象模式的相关文章

螃蟹学PHP设计模式之数据访问对象模式

3.数据访问对象模式 今天又要上班了,不过公司的事情倒不多,先花点时间回忆之前学的两个设计模式吧......好,复习完了,开始学习新的数据访问对象模式.螃蟹发现其实之前的项目基本都用上了数据访问对象模式,因为在程序中手写sql实在是效率不高且不易于维护.这就好比你有个亲戚在美国,你每个月都想送点东西给他,然后你每次都要做飞机汽车跑到他哪儿去,东西给他再风尘仆仆地回来,结果有一天你突然发现有快递这服务,你每次把东西交给快递就啥事不管了,由此感叹真方便啊.其实螃蟹也觉得数据访问对象模式真的如同快递员

Java多线程编程之不可变对象模式

在多线程环境中,为了保证共享数据的一致性,往往需要对共享数据的使用进行加锁,但是加锁操作本身就会带来一定的开销,这里可以使用将共享数据使用不可变对象进行封装,从而避免加锁操作. 1. 模型角色 不可变对象指的是,对象内部没有提供任何可供修改对象数据的方法,如果需要修改共享变量的任何数据,都需要先构建整个共享对象,然后对共享对象进行整体的替换,通过这种方式来达到对共享对象数据一致性的保证.如下是不可变对象设计的类图: 如下是各个角色功能的描述: ImmutableObject:不可变对象的载体.对

PHP设计模式系列 - 数据访问对象模式

数据访问对象模式 数据访问对象模式描述了如何创建透明访问数据源的对象. 场景设计 设计一个BaseDao基类,实现数据库操作基本的一些query,insert,update方法 在实际使用的过程中,继承BaseDao,就可以直接调用基类的数据库操作方法 代码:BaseDao 数据库操作基类 <?php //数据访问对象模式 //将数据库访问层脱离出来 作为公用的访问接口,方便用户开放,是php中常用的一种设计模式 class BaseDao { private $db; public funct

【设计模式】数据访问对象模式

数据访问对象模式(Data Access Object Pattern)或 DAO 模式用于把低级的数据访问 API 或操作从高级的业务服务中分离出来.以下是数据访问对象模式的参与者. 数据访问对象接口(Data Access Object Interface) - 该接口定义了在一个模型对象上要执行的标准操作. 数据访问对象实体类(Data Access Object concrete class) - 该类实现了上述的接口.该类负责从数据源获取数据,数据源可以是数据库,也可以是 xml,或者

多线程编程-设计模式之保护性暂挂(Guarded Suspesion)模式

Guarded Suspension模式的架构 核心是一个受保护方法(Guarded Method).该方法需要执行其所要真正执行的操作时需要满足特定的条件(Predicate,以下称之为保护条件).当该条件不满足时,执行受保护方法的线程会被挂起进入等待状态,直到该条件满足时该线程才会继续运行.此时,受保护方法才会正在执行其所要执行的操作(目标操作).GuardedObject:包含受保护方法的对象.主要方法和职责如下:-guardedMethod:受保护方法.-stateChanged:改变G

Java多线程编程实战指南(设计模式篇,黄文海)-之管道线模式

不得不说,本人工作上很少有使用多线程编程技术的地方.由于本人工作上经常使用的是类似SSH等框架搭建MVC架构,所以更加习惯于编写一些优秀程序员所唾弃的样板式的代码.最近看了文海的多线程编程实战指南,瞬间眼前一亮.觉得有很多自己可以学习的,事实上,我已经在最近的项目中使用上了那本书介绍的两相终止模式.串行封闭模式.生产者消费者模式以及线程池等技术,确实在许多方面改进了我们服务端的吞吐量.说到这里本人吐槽一下,由于是毕业后转行,目前也才工作一年还不满2个月.所以原谅我的得瑟,但我相信我以后会做的更好

Qt多线程编程总结(一)(所有GUI对象都是线程不安全的)

Qt对线程提供了支持,基本形式有独立于平台的线程类.线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法. 这个文档是提供给那些对多线程编程有丰富的知识和经验的听众的.推荐阅读: Threads Primer: A Guide to Multithreaded Programming Thread Time: The Multithreaded Programming Guide Pthreads Programming: A POSIX Standard for Be

Java多线程编程模式实战指南(二):Immutable Object模式--转载

本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-immutable-object.转载请注明作者: 黄文海 出处:http://viscent.iteye.com. 多线程共享变量的情况下,为了保证数据一致性,往往需要对这些变量的访问进行加锁.而锁本身又会带来一些问题和开销.Immutable Object模式使得我们可以在不使用锁的情况下,既保证共享变量访问的线程安

Java多线程编程模式实战指南(二):Immutable Object模式

多线程共享变量的情况下,为了保证数据一致性,往往需要对这些变量的访问进行加锁.而锁本身又会带来一些问题和开销.Immutable Object模式使得我们可以在不使用锁的情况下,既保证共享变量访问的线程安全,又能避免引入锁可能带来的问题和开销. Immutable Object模式简介 多线程环境中,一个对象常常会被多个线程共享.这种情况下,如果存在多个线程并发地修改该对象的状态或者一个线程读取该对象的状态而另外一个线程试图修改该对象的状态,我们不得不做一些同步访问控制以保证数据一致性.而这些同