Java并发递增值

Java并发递增值,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我一直在读Java中的volatile和synchronized,但一直在困惑中挠头。我希望有人能帮我解决一个问题 private HashMap<String,int> map = new HashMap<String,int>(); 我的目标是让所有线程共享这个map。如果我添加了volatile,它似乎并没有为我解决问题(我输出并看到map每次都被覆盖)。然后我尝试使用ConcurrentHashMap并在其前面添加volatile。。。这似乎也不起作用。根据我对

我一直在读Java中的
volatile
synchronized
,但一直在困惑中挠头。我希望有人能帮我解决一个问题

private HashMap<String,int> map = new HashMap<String,int>();
我的目标是让所有线程共享这个
map
。如果我添加了
volatile
,它似乎并没有为我解决问题(我输出并看到
map
每次都被覆盖)。然后我尝试使用
ConcurrentHashMap
并在其前面添加
volatile
。。。这似乎也不起作用。根据我对
volatile
的阅读,我的理解是当
map
被写入时,它应该“锁定”对
map
的访问,然后当
map
被写入时,锁被释放

所以。。。然后我尝试添加
static

private static ConcurrentHashMap<String,int> map = new ConcurrentHashMap<String,int>();
私有静态ConcurrentHashMap=new ConcurrentHashMap();
这似乎很有效。。。但是我一直在读到使用
static
不是正确的方法,因为“争用”(我不太理解)


提前感谢

Volatile
在这里没有帮助<代码>易失性对于解决可见性问题非常有用,但您面临另一个问题:原子性

哦,
volatile
与锁定完全无关。它在读/写时不会获得锁,也不会释放任何东西。它所做的是:在写入易失性字段之前发生的所有操作,在读取相同的易失性字段之后,每个其他线程都可以看到。不涉及锁(它们的相似之处在于释放/获取锁的记忆效果完全相同)

操作
get
set
不是原子操作,这意味着这两个操作之间可能会发生其他事情

例如,一个线程将
获取
该值,然后另一个线程将
获取
相同的值,两者都将增加该值,然后第一个线程将
设置
新值,然后第二个线程将执行相同的操作。最终的结果不是你所期望的

此问题最常见的解决方案是将访问(即,
同步
)序列化到共享变量,或使用比较和设置(CAS)(因此无需进行同步)

1. <代码>已同步 CAS算法将执行以下操作:

private final Map<String, AtomicInteger> m = new ConcurrentHashMap<String, AtomicInteger>();
incrementValue(final String valueName) {
  m.get(valueName).incrementAndGet();
}
for (;;) {
  state = object.getCurrentState();
  if (object.updateValueAndStateIfStateDidntChange(state)) {
    break;
  }
}
假设方法
updateValueAndStateIfStateDidntChange
是原子的,并且仅当它能够更新值时才会返回true。这样,如果另一个线程在您获取状态之后和更新值之前修改了该值,那么该方法将返回false,循环将重试

假设您可以以不使用
synchronized
(并且可以通过使用java.util.concurrent中的类)的方式实现该方法,您将避免争用(这意味着线程等待获得另一个线程持有的锁),并且您可能会看到性能的总体改善

我在我编写的分布式任务执行系统中使用了很多这种东西。所有的任务都必须执行一次,我有很多机器在执行任务。这些任务都在一个MySQL表中指定。怎么做?您必须有一个用于允许实施CAS的列。称之为执行。在开始任务之前,必须执行以下操作:检索下一个任务,
“更新任务集执行=1,其中id=:id和executing=0”
计算更新的行数。如果您更新了0行,那是因为另一个线程/进程/计算机已经执行了该任务(并成功执行了“更新”查询);在这种情况下,您会忘记它并尝试下一个任务,因为您知道这个任务已经在执行中。如果您更新了1行,那么就可以开始了,您可以执行它


我经常使用CAS的另一个地方是在我编写的一个非常动态(就其配置而言)的资源池中(我主要使用它来管理“连接”,即套接字,但它足够通用,可以容纳任何类型的资源)。基本上,它计算它拥有多少资源。当您尝试获取资源时,它会读取计数器、递减计数器、尝试更新计数器(如果其间没有任何其他修改计数器),如果成功,则您可以从池中获取资源并借出它(计数器达到0后,它将不借出资源)。如果我发布了这段代码,我肯定会在这里添加一个链接。

答案当然是使用
AtomicInteger
,而不是
int

如果您使用,则需要使用其特殊的线程安全方法

下面是该方法的javadoc摘录:

这相当于

除了动作是原子执行的

private ConcurrentMap=new ConcurrentHashMap();
原子整数值=map.get(“值”);
如果(值==null){
putIfAbsent(“value”,新的AtomicInteger());
value=map.get(“value”);
}
value.incrementAndGet();
请注意,您不必将新值放回原处


如果您对代码做了这样的更改,它应该可以正常工作。

Volatile确保线程不会缓存此值。Volatile确保从内存中写入/读取此值是原子的,但它不会在多步骤操作中锁定此变量

将映射替换为ConcurrentHashMap并使用原子CAS操作


使用:ConcurrentHashMap.putIfAbsent()

感谢您的快速响应。在一个方面仍然有点困惑:与静态地图相比,同步的好处是什么?当我将映射设置为静态时,我没有遇到一个线程重写另一个线程所做更改的问题(或者我可能是j
private final Map<String, AtomicInteger> m = new ConcurrentHashMap<String, AtomicInteger>();
incrementValue(final String valueName) {
  m.get(valueName).incrementAndGet();
}
for (;;) {
  state = object.getCurrentState();
  if (object.updateValueAndStateIfStateDidntChange(state)) {
    break;
  }
}
if (!map.containsKey(key))
    return map.put(key, value);
else
    return map.get(key);
private ConcurrentMap<String, AtomicInteger> map = new ConcurrentHashMap<String, AtomicInteger>();

AtomicInteger value = map.get("value");
if (value == null) {
    map.putIfAbsent("value", new AtomicInteger());
    value = map.get("value");
}
value.incrementAndGet();