我有一个MapOrder可以被许多不同的线程访问。我想控制访问,因此考虑以下简单的数据结构+包装器。

public interface OrderContainer {
    boolean contains(String orderId);
    Order get(String orderId);
    Order register(Order value);
    Order remove(String orderId);
    Collection<Order> getOrders();
}

public class SimpleOrderContainer implements OrderContainer {
    private Map<String, Order> orders = new ConcurrentHashMap<>();
    private Collection<Order> ordersView = Collections.unmodifiableCollection(orders.values());

    @Override
    public boolean contains(String orderId) {
        return orders.containsKey(orderId);
    }

    @Override
    public Order get(String orderId) {
        return orders.get(orderId);
    }

    @Override
    public Order register(Order value) {
        return orders.put(value.getId(), value);
    }

    @Override
    public Order remove(String orderId) {
        return orders.remove(orderId);
    }

    @Override
    public Collection<Order> getOrders() {
        return ordersView;
    }
}


很容易。现在,Order具有另一种方法getType。我希望我的班级能够按类型访问Order,但是我不想每次调用此方法都必须遍历整个地图。我要保留具有此信息的视图。

问题:


以线程安全的方式使两个视图保持一致
我尝试做#1时可能会过度同步,从而影响性能
我不能确保没有人保留对原始OrderContainer的引用,这将使我的typeView不同步。


这是我第一次参加装饰课。这种尝试几乎可以肯定过度同步:

public class TypeOrderContainer implements OrderContainer {
    private OrderContainer backing;

    private Map<String, Map<String, Order>> typeView = new ConcurrentHashMap<>();

    TypeOrderContainer(OrderContainer backing) {
        this.backing = backing;
    }

    public boolean contains(String orderId) {
        return backing.contains(orderId);
    }

    public Order get(String orderId) {
        return backing.get(orderId);
    }

    public synchronized Order register(Order value) {
        String type = value.getType();

        Map<String, Order> innerMap = getInnerMap(type);
        innerMap.put(value.getId(), value);

        return backing.register(value);
    }

    private Map<String, Order> getInnerMap(String type) {
        if(!typeView.containsKey(type)) {
            return addInnerMap(type);
        } else {
            return typeView.get(type);
        }
    }

    private Map<String, Order> addInnerMap(String type) {
        Map<String, Order> innerMap = new ConcurrentHashMap<>();

        typeView.put(type, innerMap);

        return innerMap;
    }

    public synchronized Order remove(String orderId) {
        Order order = backing.remove(orderId);

        if(order == null) return null;

        String type = order.getType();
        Map<String, Order> innerMap = getInnerMap(type);
        if(innerMap == null) {
            // I suspect this is not the best error handling logic
            throw new IllegalStateException("Somehow the inner map is out of sync!!");
        } else {
            innerMap.remove(order.getId());
            // Could do this if you want, likely not necessary in my use case
            // if (innerMap.isEmpty()) typeView.removeInnerMap();
        }

        return order;
    }

    public Collection<Order> getOrders() {
        return backing.getOrders();
    }

    public Map<String, Order> getOrdersByType(String type) {
        return Collections.unmodifiableMap(getInnerMap(type));
    }
}


有没有更好的方法来保持我的数据视图一致并且仍然是线程安全的?

最佳答案

我认为在您的情况下#3是不可能的。本质上,您想要的是在OrderContainer中插入/删除操作(不使用TypeOrderContainer装饰器)时,您希望TypeOrderContainer(尤其是typeView)同时了解插入/删除操作(请参见那里已经有向后依赖吗?)。如果这不是您要的内容,请忽略此答案的其余部分。

在您的情况下,您具有被引用的OrderContainer的完全独立的视图,其中正在建立和维护String typeCollection<Order> typedOrders之间的关系。通过OrderContainer中声明的方法,我们最多可以假定存在Collection<Order> allOrders。现在,让我们想象一下typeOrders1typeOrders2,它们一起形成allOrders。如果我在allOrders中插入订单,allOrders的集合如何知道将新订单放入哪个typeOrder?答案是allOrders除非理解类型-> Orders的关系,否则它不会,并且如果理解,它将破坏此装饰器的作用。最终,除非子集了解要查找的范围,并且allOrders没有“类型”范围,否则子集将无法理解对原始集的更新。如果您有兴趣,java的TreeSet.subSet有一个非常相似的问题,其中不会显示超出指定subSet范围的任何添加。

另一方面,我相信您的解决方案已经针对您的目的进行了精心设计。我要进行的一种优化是减少同步。唯一需要同步typeView的时间是在插入全新的类型时:

// This assumes your maps are ConcurrentHashMaps
public Order register(Order value) { // No synchronized here since CHM does it for you
    Map<String, Order> innerMap = getInnerMap(type);
    innerMap.put(value.getId(), value);

    return backing.register(value);
}

private Map<String, Order> getInnerMap(String type) {
    if (!typeView.containsKey(type)) {
        synchronized(typeView)  {
            if (!typeView.containsKey(type)) { // make sure no one else snuck in after you "checked"
                typeView.put(type, new ConcurrentHashMap<>());
            }
        }
    }
    return typeView.get(type);
}

public Order remove(String orderId) { // No synchronized here since CHM does it for you
    Order order = backing.remove(orderId);

    if(order == null) return null;

    String type = order.getType();
    Map<String, Order> innerMap = getInnerMap(type);
    if(innerMap == null) {
        // I suspect this is not the best error handling logic
        throw new IllegalStateException("Somehow the inner map is out of sync!!");
    } else {
        innerMap.remove(order.getId());
        // Could do this if you want, likely not necessary in my use case
        // if (innerMap.isEmpty()) typeView.removeInnerMap();
    }

    return order;
}

public Collection<Order> getOrders() {
    return backing.getOrders();
}

public Map<String, Order> getOrdersByType(String type) {
    return Collections.unmodifiableMap(getInnerMap(type));
}


另外,一个typical decorator pattern示例将表明这通常是可能的,因为装饰器类仅包装了受支持的对象(在您的情况下为OrderContainer backing),并且重写的方法将附加内容应用于受支持的对象。但是,由于您基本上是在将OrderContainer转换(/分解)为另一个视图,所以这对您来说不太可行。

08-05 18:31