17

在使用多个线程时,我学会了在想要使用将由多个线程访问的计数器时使用静态变量。

例子:

static int count=0;然后稍后在程序中我将其用作count++;.

今天我遇到了一个叫做的东西AtomicInteger,我也了解到它是线程安全的,可以使用它的一种方法getAndInrement()来实现相同的效果。

任何人都可以帮助我了解使用static atomicIntegervsstatic int count吗?

4

7 回答 7

25

- AtomicInteger用于对整数执行原子操作,当您不想使用synchronized关键字时,它是一种替代方法。

-volatile在非原子字段上使用 a会产生不一致的结果。

int volatile count;

public void inc(){

count++

}

- static将使该类的所有实例共享一个变量,但在多线程环境中仍然会产生不一致的结果。

所以当你在多线程环境中时试试这些:

1.遵循布赖恩法则总是更好:

当我们写一个变量接下来要被另一个线程读取时,或者当我们正在读取一个刚刚被另一个线程写入的变量时,它需要同步。共享字段必须设为私有,使读写方法/原子语句同步。

2.第二个选项是使用Atomic Classes, likeAtomicInteger, AtomicLong, AtomicReference, etc.

于 2012-11-28T06:17:53.777 回答
9

我同意@Kumar 的回答。

Volatile 是不够的——它对内存顺序有一些影响,但不能确保 ++ 的原子性。

多线程编程的真正困难在于,问题可能不会出现在任何合理数量的测试中。我编写了一个程序来演示这个问题,但它的线程除了递增计数器之外什么都不做。即便如此,计数仍然在正确答案的 1% 以内。在一个线程有其他工作要做的实际程序中,两个线程执行 ++ 的可能性非常低,足以同时显示问题。多线程的正确性无法测试,它必须被设计。

该程序使用简单的静态整数、易失整数和 AtomicInteger 执行相同的计数任务。只有 AtomicInteger 始终得到正确答案。具有 4 个双线程内核的多处理器上的典型输出是:

count: 1981788 volatileCount: 1982139 atomicCount: 2000000 Expected count: 2000000

这是源代码:

  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.atomic.AtomicInteger;

  public class Test {
    private static int COUNTS_PER_THREAD = 1000000;
    private static int THREADS = 2;

    private static int count = 0;
    private static volatile int volatileCount = 0;
    private static AtomicInteger atomicCount = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
      List<Thread> threads = new ArrayList<Thread>(THREADS);
      for (int i = 0; i < THREADS; i++) {
        threads.add(new Thread(new Counter()));
      }
      for (Thread t : threads) {
        t.start();
      }
      for (Thread t : threads) {
        t.join();
      }
      System.out.println("count: " + count +  " volatileCount: " + volatileCount + " atomicCount: "
          + atomicCount + " Expected count: "
          + (THREADS * COUNTS_PER_THREAD));
    }

    private static class Counter implements Runnable {
      @Override
      public void run() {
        for (int i = 0; i < COUNTS_PER_THREAD; i++) {
          count++;
          volatileCount++;
          atomicCount.incrementAndGet();
        }
      }
    }
  }
于 2012-11-28T07:01:49.470 回答
2

保证是原子AtomicInteger的。 如果您用于获取先前的值,则不能保证它是原子的。 incrementAndGet()
count++


我从您的问题中遗漏了一些东西 - 并且由其他答案说明 - 静态与线程无关。

于 2012-11-28T05:54:34.617 回答
2

“静态”使 var 成为类级别。这意味着,如果您在一个类中定义“静态整数计数”,那么无论您为该类创建了多少个实例,所有实例都使用相同的“计数”。虽然 AtomicInteger 是一个普通类,但它只是添加了同步保护。

于 2012-11-28T05:59:41.853 回答
1

static int countermultithreaded除非您制作计数器volatile或制作增量块,否则会在环境中给您带来不一致的结果synchronized

如果automic它给出了lock-free thread-safe对单个变量的编程。

automic链接中的更多详细信息

于 2012-11-28T06:01:05.190 回答
1

我认为没有保证可以看到count++最新的值。count++必须读取 的值count。另一个Thread可以写入一个新值,count但将它的值存储在Thread本地缓存中,即不刷新到主内存。同样,您的Thread,读取count,没有保证从主存储器读取,即从主存储器刷新。synchronize保证。

于 2012-11-28T06:08:43.157 回答
1

AtomicInteger 就是让get和increment成为一个原子过程。它可以被认为是数据库中的排序器。它提供了增加、减少 delta int 值的实用方法。

如果您获取计数器然后处理然后更新它,静态 int 可能会导致问题。AtomicInteger 可以轻松完成,但如果您必须根据处理结果更新计数器,则无法使用它。

于 2012-11-28T06:08:54.520 回答