Java 我们能在方法中使用AtomicInteger作为局部变量并实现线程安全吗? public void tSafe(列表、属性状态){ if(list==null)返回; String key=“COUNT”; AtomicInteger a=新的AtomicInteger(Integer.valueOf(status.getProperty(key,“0”)); list.parallelStream().filter(Foo::check)。 forEach(foo->{status.setProperty(key,String.valueOf(a.incrementAndGet());} ); } 专用接口Foo{ 公共布尔检查(); }

Java 我们能在方法中使用AtomicInteger作为局部变量并实现线程安全吗? public void tSafe(列表、属性状态){ if(list==null)返回; String key=“COUNT”; AtomicInteger a=新的AtomicInteger(Integer.valueOf(status.getProperty(key,“0”)); list.parallelStream().filter(Foo::check)。 forEach(foo->{status.setProperty(key,String.valueOf(a.incrementAndGet());} ); } 专用接口Foo{ 公共布尔检查(); },java,multithreading,Java,Multithreading,说明: 在上面的示例中,status是一个共享属性,它包含一个名为COUNT的键。我的目标是增加计数并将其放回属性中,以计算执行的检查数。考虑TFAID方法是由多个线程调用的,在结尾处我得到正确的计数吗?请注意,我使用了AtomicInteger a作为局部变量。如果只有一个线程,这将起作用,但是如果有多个线程调用它,则会有一些线程安全的操作。如果每个线程在不同的列表和状态对象上运行,这将是很好的 由于status是线程安全的集合,您可以锁定它,如果列表在另一个线程中没有更改,则这将是安全的 一

说明:


在上面的示例中,status是一个共享属性,它包含一个名为COUNT的键。我的目标是增加计数并将其放回属性中,以计算执行的检查数。考虑TFAID方法是由多个线程调用的,在结尾处我得到正确的计数吗?请注意,我使用了AtomicInteger a作为局部变量。

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

由于
status
是线程安全的集合,您可以锁定它,如果
列表
在另一个线程中没有更改,则这将是安全的


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

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

考虑以下场景:

  • 线程#1从
    属性
    对象获取一个值。为了辩论起见,我们假设它是“100”
  • 线程2从
    属性
    对象获取一个值。由于没有发生任何情况,该值仍然为“100”
  • 线程#1创建一个
    原子整数
    ,将其递增,并在
    属性
    对象中放置“101”
  • 线程#2执行完全相同的操作,并将“101”放置在
    Properties
    对象中,而不是预期的102
  • 编辑:
    更有效的方法是,将
    原子整数存储在状态图上,并在原地递增。这样,您就有了一个实例,不必像上面描述的那样担心种族问题。随着
    Properties
    类扩展
    Hashtable
    这在技术上应该是可行的,尽管
    Properties
    实际上并不适用于那些不是
    String
    s的值,使用一个现代的线程安全
    Map
    实现,例如
    ConcurrentHashMap
    ,会更好:

     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();
    }
    
    public void tSafe(列表、ConcurrentMap状态){
    if(list==null){
    返回;
    }
    String key=“COUNT”;
    putIfAbsent状态(键,新原子整数(0));
    list.parallelStream()
    .filter(Foo::check)
    .forEach(foo->{status.get(ket).incrementAndGet();});
    }
    
    So。。我是否需要将AtomicInteger作为静态最终变量,而不是将其用作局部变量?使用Atomic*实现线程安全的最佳实践是什么?@aravindpogu我试图描述一种更好的方法(当然是IMHO)——请参阅我编辑的答案。需要键作为第一个参数,然后是值:
    status.putIfAbsent(ket,new-AtomicInteger(0))愚蠢的遗漏。。。不幸的是,我打字的速度比我想象的要快得多:-(.谢谢你注意到@Ferrybig(当然是经过编辑和修复的).@aravindpogu,变量与对象不是一回事。如果两个或多个线程共享一个
    AtomicInteger
    对象,那么线程访问该对象所使用的变量类型或变量数量都无关紧要。线程安全性并不是指围绕多线程构造的问题生产线安全。
    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(); });
    }