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

定义: 将现实世界中状态可变的实体建模为状态不可变对象,并通过创建不同的状态不可变的对象来反映实现世界实体的状态变更

不可变对象模式UML图

ImmutableObject:负责存储一组不可变状态。该参与者不对外暴露任何可以修改其状态的方法
    getStateX, getStateN:这些getter方法返回其所属ImmutableObject实例所维护的状态相关变量的值。这些变量在对象实例化时通过其构造器的参数获得值
    getStateSnapshot:返回其所属ImmutableObject实例维护的一组状态的快照
Manipulator:负责维护ImmutableObject所建模的显示世界实体状态的变更。当相应的现实实体状态变更时,该参与者负责生成新的ImmutableObject的实例,以反映新的状态
    changeStateTo: 根据新的状态值生成新的ImmutableObject的实例

一个严格意义上的不可变对象要满足以下所有条件
1. 类本身使用final修饰,防止其子类改变其定义的行为
2. 所有字段都是用final修饰的,保证在多线程环境下由JMM(Java Memory Model)保证了被修饰字段所引用对象的初始化安全
3. 在对象的创建过程中,this关键字没有泄露给其他类,防止其他类(比如该类的内部匿名类)在对象创建过程中修改其状态
4. 任何字段,若其引用了其他状态可变的对象(如集合、数组等),则这些字段必须是private修饰的,并且这些字段值不能对外暴露

下列代码中MMSCInfo为一个不可变对象,MMSCRouter通过方法setInstance来创建新的MMSCRouter实例,再加上volatile修饰,从而达到线程安全的目的。

package com.bruce.immutableObject;

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

/**
* @Author: Bruce
* @Date: 2019/5/30 19:10
* @Version 1.0
*/
public final class MMSCRouter {

    private static volatile MMSCRouter instance = new MMSCRouter();

    private final Map<String, MMSCInfo> routeMap;

    public MMSCRouter() {
        this.routeMap = MMSCRouter.retrieveRouteMapFromDB();
    }

    private static Map<String, MMSCInfo> retrieveRouteMapFromDB() {
        Map<String, MMSCInfo> map = new HashMap<String, MMSCInfo>();
        System.out.println("Retrieve data from DB");
        return map;
    }

    public static MMSCRouter getInstance() {
        return instance;
    }

    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<String, MMSCInfo>();
        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.bruce.immutableObject;

/**
* @Author: Bruce
* @Date: 2019/5/30 19:16
* @Version 1.0
*/
public final class MMSCInfo {

    private final String deviceID;

    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.bruce.immutableObject;

/**
* @Author: Bruce
* @Date: 2019/5/30 19:19
* @Version 1.0
*/
public class OMCAgent extends Thread {

    @Override
    public void run() {
        boolean isTableModificationMsg = false;
        String updatedTableName = null;

        while (true) {
            System.out.println("OMCAgent running");
            if (isTableModificationMsg) {
                if ("MMSCInfo".equals(updatedTableName)) {
                    MMSCRouter.setInstance(new MMSCRouter());
                }
            }
        }
    }
}

详细源码可见github

应用场景:
1.被建模对象的状态不频繁
2.同时对一组相关的数据进行写操作,因此需要保证原子性
3.使用某个对象作为安全的HashMap的key

注意事项:
1.被建模对象的状态变更比较频繁
2.使用等效或者近似的不可变对象
3.防御性复制

java.util.concurrent.CopyOnWriteArrayList应用了不可变对象模式,实现了不用加锁也能保证线程安全,专门针对遍历操作的频率比添加删除操作更加频繁的场景而设计

参考资料

黄文海 Java多线程编程实战指南(设计模式篇)

黄文海的Github

猜你喜欢

转载自blog.csdn.net/u010145219/article/details/91070624