在使用多个线程时,每当我想使用一个可被多个线程访问的计数器时,我就学会了使用静态变量。
示例:
static int count=0;
然后在程序中我将其用作count++;
。
今天我遇到了一个名为AtomicInteger
的东西,我也了解到它是线程安全的,可以使用一个名为getAndInrement()
的方法来达到同样的效果。
有人可以帮我理解使用static atomicInteger
与static int count
的对比吗?
答案 0 :(得分:25)
- AtomicInteger
用于对整数执行原子操作,当您不想使用{{1}时,它是另一种选择关键字。
- 在非原子字段上使用synchronized
会产生不一致的结果。
volatile
- int volatile count;
public void inc(){
count++
}
会使该类的所有实例共享一个变量,但仍然会在多个实体中产生不一致的结果线程环境。
在多线程环境中尝试这些:
1。遵循Brian的规则总是更好:
当我们编写一个接下来要被另一个读取的变量时 线程,或者当我们读取一个仅由其编写的变量时 另一个线程,它需要同步。共享字段必须是 私有化,制作读写方法/原子语句 同步。
2。第二个选项正在使用static
,例如Atomic Classes
答案 1 :(得分:8)
我同意@ Kumar的回答。
易失性是不够的 - 它对内存顺序有一些影响,但不能确保++的原子性。
多线程编程的真正困难之处在于,任何合理数量的测试都不会出现问题。我写了一个程序来演示这个问题,但它有一些线程除了增加计数器之外什么都不做。即便如此,计数仍在正确答案的1%左右。在一个真正的程序中,线程还有其他工作要做,两个线程执行++接近足以同时显示问题的可能性非常低。无法测试多线程正确性,必须进行设计。
该程序使用简单的静态int,volatile int和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();
}
}
}
}
答案 2 :(得分:2)
AtomicInteger
incrementAndGet()
保证原子。{
如果使用count++
来获取先前的值,则不能保证它是原子的。
我错过了你的问题 - 并且通过其他答案说明 - 静态与线程无关。
答案 3 :(得分:1)
“static”使var成为类级别。这意味着,如果在类中定义“static int count”,无论您创建了多少个类实例,所有实例都使用相同的“count”。虽然AtomicInteger是普通类,但它只是添加了同步保护。
答案 4 :(得分:1)
static int counter
会在multithreaded
环境中为您提供不一致的结果,除非您使计数器volatile
或增量块synchronized
。
如果是automic
,则会对单个变量进行lock-free
thread-safe
编程。
答案 5 :(得分:1)
我认为没有保证可以在count++
上看到最新的价值。 count++
必须读取count
的值。另一个Thread
可以为count
写入一个新值,但将其值存储在Thread
本地缓存中,i。即不会刷新到主存储器。另外,你的Thread
读取count
,没有保证从主存中读取,i。即从主内存刷新。 synchronize
保证这一点。
答案 6 :(得分:1)
AtomicInteger将get和increment作为原子进程。它可以被认为是数据库中的序列发生器。它提供了实用程序方法来递增,递减delta int值。
如果你得到计数器然后处理然后更新它,static int会导致问题。 AtomicInteger可以轻松完成,但如果您必须根据处理结果更新计数器,则无法使用它。