public void tSafe(List<Foo> list, Properties status) {
if(list == null) return;
String key = "COUNT";
AtomicInteger a = new AtomicInteger(Integer.valueOf(status.getProperty(key,"0")));
list.parallelStream().filter(Foo::check).
forEach(foo -> {status.setProperty(key, String.valueOf(a.incrementAndGet())); }
);
}
private interface Foo {
public boolean check();
}
说明
在上面的示例中,status是共享属性,它包含名为COUNT的键。我的目标是增加计数并将其放回属性中以计算执行的检查次数。考虑多个线程调用tSafe方法,我最后得到正确的计数吗?请注意,我已将AtomicInteger用作局部变量。
答案 0 :(得分:0)
如果你只有一个线程,这将有效,但是如果你有多个线程调用它,你有一些线程安全的操作。如果每个线程在不同的list
和status
对象上运行,这将很好。
由于status
是一个线程安全的集合,你可以锁定它,如果list
在另一个线程中没有改变,那就可以了。
通常,以线程安全的方式使用String作为数字是非常棘手的。你最好不要制作你的价值线程,即AtomicInteger而不是其他任何东西。
答案 1 :(得分:0)
不,这不能保证线程安全。尽管incrementAndGet()
本身就是原子的,但从Properties
对象获取值并将其设置回来却不是。
考虑以下情况:
Properties
对象获取值。为了论证,让我们说它是“100”。Properties
对象获取值。由于什么都没发生,这个值仍然是“100”。AtomicInteger
,递增它,并在Properties
对象中放置“101”。Properties
对象中放置“101”,而不是您期望的102. 编辑:
更有效的说明是,更好的方法是将AtomicInteger
存储在您的状态图上,然后将其增加到位。这样,您就拥有一个实例,而不必担心如上所述的比赛。由于Properties
类扩展Hashtable<Object, Object>
,这在技术上应该可行,但Properties
实际上不适用于非String
s的值,并且您会更好使用现代线程安全Map
实现,例如ConcurrentHashMap
:
public void tSafe(List<Foo> list, ConcurrentMap<String, AtomicInteger> status) {
if(list == null) {
return;
}
String key = "COUNT";
status.putIfAbsent(key, new AtomicInteger(0));
list.parallelStream()
.filter(Foo::check)
.forEach(foo -> { status.get(ket).incrementAndGet(); });
}