我有一个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
。我希望我的班级能够按类型访问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 type
和Collection<Order> typedOrders
之间的关系。通过OrderContainer
中声明的方法,我们最多可以假定存在Collection<Order> allOrders
。现在,让我们想象一下typeOrders1
和typeOrders2
,它们一起形成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转换(/分解)为另一个视图,所以这对您来说不太可行。