如何解决此类线程如何安全? 实践中的并发
我正在阅读《实践中的并发性》,并且碰到了此类:
@ThreadSafe
public class PublishingVehicleTracker {
private final Map<String,SafePoint> locations;
private final Map<String,SafePoint> unmodifiableMap;
public PublishingVehicleTracker(Map<String,SafePoint> locations) {
this.locations = new ConcurrentHashMap<String,SafePoint>(locations);
this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
}
public Map<String,SafePoint> getLocations() {
return unmodifiableMap;
}
public SafePoint getLocation(String id) {
return locations.get(id);
}
public void setLocation(String id,int x,int y) {
if (!locations.containsKey(id))
throw new IllegalArgumentException("invalid vehicle name: " + id);
locations.get(id).set(x,y);
}
}
我的问题涉及setLocation。我了解它正在利用ConcurrentMap,但它仍在执行“先检查后行动”操作,作者承认(在下一页上)这是不安全的,并发集合事件。这不是原子动作。这个方法/类线程如何安全?
解决方法
locations
映射是只读的。该类中没有可添加到locations
映射中或从中删除元素的操作。这样一来,该类的最后一部分就可以设置位置了。
setLocation
方法从locations
映射中获取一个元素,然后更新其中的现有值。这意味着,如果SafePoint.set
是线程安全的,则整个类都是线程安全的。
请注意,这不是先检查后行动的情况。它检查值是否在映射中,但不修改映射本身,它仅修改值。另一个线程无法从映射中删除该值,或将另一个值添加到映射中。因此,线程安全取决于set
是线程安全的。
在大多数情况下,对象不是线程安全的,因为对象的状态更改没有得到适当的保护。
阅读我从“ Java Concurrency in Practice”中摘录的以下段落:
无状态:它没有字段,也没有引用其他类的字段。特定计算的暂态仅存在于局部变量中,这些局部变量存储在线程的堆栈中,并且只有执行线程才能访问。 无状态对象始终是线程安全的。
第2章还分享了一些使有状态对象线程安全的方法。
有三种方法可以修复有状态对象:
- 不要在线程之间共享状态变量;
- 使状态变量不可变;或
- 在访问状态变量时使用同步。
在上面的示例中,locations
是并发哈希表,它是一个线程安全的集合。初始化后无法对unmodifiableMap
进行突变。因此,该类是线程安全的。
setLocation使用getLocation()。这将从位置返回一个现有条目。
两个不同的线程可以并行调用setLocation()并可以调用SafePoint.set()。 如果这是线程安全的,则取决于SafePoint.set()。如果SafePoint.set()需要更多的时间并且不是原子的,则可能在线程1通过SafePoint.set()进行写入时发生,而另一个线程也正在这样做。
如果您只能通过setLocation()访问SafePoint,则可以这样做:
public void setLocation(String id,int x,int y) {
if (!locations.containsKey(id))
throw new IllegalArgumentException("invalid vehicle name: " + id);
SafePoint safePoint = locations.get(id);
synchronized(safePoint){
safePoint.set(x,y);
}
}
每个调用setLocation的线程都将进入同步块。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。