我有Map
个Order
个,可由许多不同的线程访问。我想控制访问,所以请考虑以下简单的数据结构+包装器。
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
。我想decorate我的班级能够按类型访问Order
,但我不希望每次调用此方法时都要迭代整个地图;我想保留一个包含此信息的视图。
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));
}
}
是否有更好的方法可以保持数据视图的一致性并保持线程安全?
答案 0 :(得分:1)
在你的情况下我不相信#3是可能的。基本上你想要的是当OrderContainer
中发生插入/删除(不使用你的TypeOrderContainer
装饰器)时,你想要TypeOrderContainer
(特别是你的typeView)知道插入/删除同时(看看那里已经存在向后依赖?)。如果这不是您要求的,请忽略此答案的其余部分。
在您的情况下,您对引用的OrderContainer
有一个完全独立的视图,其中正在构建和维护String type
和Collection<Order> typedOrders
之间的关系。通过OrderContainer
中声明的方法,我们最多可以假设有Collection<Order> allOrders
。现在让我们假设有typeOrders1
和typeOrders2
,它们一起形成allOrders
。如果我将订单插入allOrders
,allOrders
的集合如何知道将typeOrder
放入新订单?答案是allOrders
除非理解类型的关系,否则// 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));
}
将不会...订单,如果确实如此,它会破坏这个装饰器的重点。最终,除非子集理解了它所寻找的范围,否则不可能让子集理解对原始集的更新,并且你的allOrders没有“类型”范围。如果您有兴趣,java的TreeSet.subSet有一个非常类似的问题,其中任何超出指定子集范围的添加都不会出现。
另一方面,我相信您的解决方案已经完全适合您的目的。我要做的一个优化是减少同步。您需要同步typeView的唯一一次是在插入全新类型时:
OrderContainer backing
此外,一个typical decorator pattern示例将显示这通常是可能的,因为装饰器类只是环绕备份对象(在您的情况下为{{1}}),并且重写方法应用添加到支持的对象。但是,由于您基本上将OrderContainer转换(/拆分)到另一个视图,这对您来说并不适用。
答案 1 :(得分:0)
如果您不想为每个调用筛选原始地图,为什么不将另一个类型的地图保留到订单中,每次更改原始地图时都会更新相应的类型地图?进一步思考,我当然会质疑同步两个集合然后迭代一个集合并“动态”构建副本是否更复杂。