英文:
How is this class thread safe? (Concurrency in Practice)
问题
我理解您的问题涉及到setLocation
方法。我明白它正在利用ConcurrentMap
,但它仍然执行一个“检查然后操作”的操作,这正如作者在下一页中承认的,即使使用并发集合,这也是不安全的。这不是一个原子操作。这个方法/类如何保证线程安全?
【翻译】
关于setLocation
方法,尽管它使用了ConcurrentMap
,但它仍然执行了一个“检查后操作”的操作,这正如作者在后续页面中所承认的,即使在使用并发集合时,这也是不安全的。这不是一个原子操作。那么这个方法/类如何确保线程安全呢?
英文:
I'm reading Concurrency in Practice and I came across this class:
@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);
}
}
My question involves setLocation. I understand it's utilizing a ConcurrentMap, but it's still performing a "check-then-act" operation, which the authors acknowledge (on the following page) is unsafe, event with concurrent collections. This isn't an atomic action. How is this method/class thread safe?
答案1
得分: 3
locations
map 是只读的。该类中没有添加或移除 locations
map 元素的操作。这使得类的最后部分设置位置。
setLocation
方法从 locations
map 获取元素,然后更新其中已经存在的值。这意味着,如果 SafePoint.set
是线程安全的,整个类就是线程安全的。
请注意,这不是一种“检查然后操作”的情况。它检查值是否在映射中,但然后不修改映射本身,只修改值。没有其他线程可以从映射中移除该值,或者向映射中添加其他值。因此,线程安全取决于 set
是否是线程安全的。
英文:
The locations
map is read only. There are no operations in the class that add to, or remove elements from the locations
map. That leaves the last part of the class where it sets the location.
The setLocation
method gets an element from the locations
map, and then updates an already existing value in it. That means, if SafePoint.set
is thread-safe, the whole class is thread-safe.
Note that this is not a check-then-act situation. It checks if the value is in the map, but then does not modify the map itself, it only modifies the value. There is no way another thread can remove that value from the map, or add another value to the map. So, thread-safety is depending on set
being thread-safe.
答案2
得分: 0
大多数情况下,对象不是线程安全的,因为其状态变化没有得到适当的保护。
阅读下面这段摘自《Java并发实践》的文字:
> 无状态:它没有字段,也不引用其他类的字段。特定计算的临时状态仅存在于存储在线程堆栈上的局部变量中,这些局部变量仅对执行线程可访问。无状态对象始终是线程安全的。
第2章还介绍了一些使有状态对象线程安全的方法。
> 有三种修复有状态对象的方法:
> - 不要在线程之间共享状态变量;
> - 使状态变量成为不可变的;或者
> - 在访问状态变量时始终使用同步。
在上面的示例中,locations
是一个并发哈希映射,它是线程安全的集合。unmodifiableMap
在初始化后无法修改。因此,该类是线程安全的。
英文:
Most of the time, object is not thread-safe because its state changes are not properly guarded.
Read the following paragraph which I took from "Java Concurrency in Practice" :
> Stateless: it has no fields and references no fields from other classes. The transient state for a particular computation exists solely in local variables that are stored on the thread’s stack and are accessible only to the executing thread. Stateless objects are always thread-safe.
Chapter 2 also shares some ways to make stateful objects thread-safe.
> There are three ways to fix stateful objects:
> - Don’t share the state variable across threads;
> - Make the state variable immutable; or
> - Use synchronization whenever accessing the state variable.
In the above example, locations
is a concurrent hashmap, it's a threadsafe collection. unmodifiableMap
can't be mutated after initialization. Hence the class is thread-safe.
答案3
得分: -2
setLocation正在使用getLocation()。这会从locations中返回一个现有的条目。
两个不同的线程可以并行调用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("无效的车辆名称:" + id);
SafePoint safePoint = locations.get(id);
synchronized(safePoint){
safePoint.set(x, y);
}
}
每个调用setLocation的线程都会进入同步块。只有一个线程可以进入此块。
英文:
setLocation is using getLocation(). This is returning an existing entry from locations.
Two differnt threads could call setLocation() in parallel and can call SafePoint.set().
It depends on SafePoint.set() if this is Thread-Safe. If SafePoint.set() needs more time and is not atomic, it can happen while Thread 1 is writing via SafePoint.set() another is intending it, too.
If you can only access to SafePoint via setLocation() you could do it like that:
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);
}
}
Every thread calling setLocation will come to the synchronized block. Only one can enter this block.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论