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

Posted

技术标签:

【中文标题】我们可以在方法中使用 AtomicInteger 作为局部变量并实现线程安全吗?【英文标题】:Can we use AtomicInteger as a local variable in a method and achieve thread safety? 【发布时间】:2016-06-30 12:35:53 【问题描述】:
 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 a 作为局部变量。

【问题讨论】:

线程安全并不是那种在各种多线程结构周围散布会产生线程安全的东西。 【参考方案1】:

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

由于status 是一个线程安全的集合,您可以锁定它,并且如果list 没有在另一个线程中更改,则可以这样做。

一般来说,以线程安全的方式将字符串用作数字是非常棘手的。你最好让你的价值线程,即 AtomicInteger 而不是其他任何东西。

【讨论】:

【参考方案2】:

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

考虑以下场景:

    线程#1 从Properties 对象中获取一个值。为了论证的缘故,假设它是“100”。 线程#2 从Properties 对象中获取一个值。由于什么都没有发生,所以这个值仍然是“100”。 线程#1 创建一个AtomicInteger,将其递增,并将“101”放入Properties 对象中。 线程 #2 完全相同,并将“101”放在 Properties 对象中,而不是您预期的 102。

编辑: 更高效的方法是,将AtomicInteger 存储在您的状态图上,并在原地增加它。这样,您只有一个实例,而不必担心如上所述的比赛。由于Properties 类扩展了Hashtable&lt;Object, Object&gt;,这在技术上应该是可行的,尽管Properties 确实不适用于不是Strings 的值,并且使用现代线程安全@987654334 会更好@实现,比如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(); );

【讨论】:

所以.. 我需要将 AtomicInteger 作为静态最终变量,而不是将其用作局部变量吗?使用 Atomic* 实现线程安全的最佳实践是什么? @aravindpogu 我试图描述一种更好的方法(当然,恕我直言) - 请参阅我编辑的答案。 putIfAbsent 需要键作为第一个参数,然后是值:status.putIfAbsent(ket,new AtomicInteger(0)); 愚蠢的遗漏...我打字的速度比我想象的要快得多,不幸的是:-(。感谢您注意到@Ferrybig(当然,已编辑和修复)。 @aravindpogu,变量与对象不同。如果您有两个或多个线程共享一个 AtomicInteger 对象,则线程使用何种变量或多少变量来访问该对象并不重要。

以上是关于我们可以在方法中使用 AtomicInteger 作为局部变量并实现线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

[Android Pro] AtomicInteger的用法

浅析CAS与AtomicInteger原子类

AtomicInteger

AtomicInteger类中getAndIncrement方法中的spin是啥意思?

无锁atomicInteger

详解java并发原子类AtomicInteger(基于jdk1.8源码分析)