我们可以在方法中使用AtomicInteger作为局部变量并实现线程安全吗?

时间:2016-03-15 07:33:53

标签: java multithreading

 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用作局部变量。

2 个答案:

答案 0 :(得分:0)

如果你只有一个线程,这将有效,但是如果你有多个线程调用它,你有一些线程安全的操作。如果每个线程在不同的liststatus对象上运行,这将很好。

由于status是一个线程安全的集合,你可以锁定它,如果list在另一个线程中没有改变,那就可以了。

通常,以线程安全的方式使用String作为数字是非常棘手的。你最好不要制作你的价值线程,即AtomicInteger而不是其他任何东西。

答案 1 :(得分:0)

不,这不能保证线程安全。尽管incrementAndGet()本身就是原子的,但从Properties对象获取值并将其设置回来却不是。

考虑以下情况:

  1. 线程#1从Properties对象获取值。为了论证,让我们说它是“100”。
  2. 线程#2从Properties对象获取值。由于什么都没发生,这个值仍然是“100”。
  3. 线程#1创建AtomicInteger,递增它,并在Properties对象中放置“101”。
  4. 线程#2完全相同,并在Properties对象中放置“101”,而不是您期望的102.
  5. 编辑:
    更有效的说明是,更好的方法是将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(); });
    }