我想跟踪在synchronized
块内被修改的队列中的位置。因此我需要一个计数器变量。通常我会用AtomicInteger
,但这里需要吗?
PriorityBlockingQueue<TfIdfScore> allScores = sharedFeatureNameToScores.get(featureName);
synchronized (allScores) {
AtomicInteger position = positionCounterMap.get(featureName);
position.getAndAdd(1);
// Do other stuff..
}
或者我也可以用int
或Integer
?synchronized
块保护我在块内的所有动作吗?
在这个例子中,position
和allScores
依赖于同一个featureName
如果您正在编写所有的代码(并采取适当的注意),那么您不需要同时使用synchronized
和原子类型。只要确保给定映射上的所有操作及其包含的计数器在执行此操作时在同一对象上同步即可……这部分代码应该是线程安全的。
另一方面,如果您担心有人会忘记同步,那么原子类型可能无法解决问题。更好的解决方案是确保映射和计数器被很好地封装起来,以减少出错的可能性。(如果你能减少可以访问状态的代码的数量,那就减少了你需要检查线程安全的地方的数量。)
同步块是否保护我在块内的所有动作?
不一定。如果有其他代码访问或更新数据结构,并且代码没有在正确的互斥锁上同步,则仍然可能存在线程安全问题;例如,同一个allScores
实例
Synchronized比atomic或volatile更严格。因此,不需要在synchronized
中使用atomic。同步同步方法为防止线程干扰和内存一致性错误提供了一个简单的策略:如果一个对象对多个线程可见,那么对该对象变量的所有读写都通过同步方法完成。波动
原子volatile字段表示该变量不会被缓存到处理器的核心/线程中。因此,每个内核/线程只有一个变量副本
java.util.concurrent.atomic包定义了支持对单个变量进行原子操作的类。所有类都有get和set方法,就像对volatile变量进行读写一样。也就是说,set与同一变量上的任何后续get之间存在happens-before关系。原子的compareAndSet方法也具有这些内存一致性特性,就像应用于整数原子变量的简单原子算术方法一样。
在您的代码中,您使用两个不同的对象使用featureName: sharedFeatureNameToScores
和positionCounterMap
。
为了保证你的代码是线程安全的,你需要确保对它们的修改都是使用相同的锁(代码中的synchronized (allScores)
)。一旦您满足了这个要求,就不需要使用AtomicInteger了,因为同步块保护了两者,所以对positionCounterMap
的访问是在独占模式下进行的。