在使用多个线程时,我学会了在想要使用将由多个线程访问的计数器时使用静态变量。
例子:
static int count=0;
然后稍后在程序中我将其用作count++;
.
今天我遇到了一个叫做的东西AtomicInteger
,我也了解到它是线程安全的,可以使用它的一种方法getAndInrement()
来实现相同的效果。
任何人都可以帮助我了解使用static atomicInteger
vsstatic int count
吗?
- AtomicInteger
用于对整数执行原子操作,当您不想使用synchronized
关键字时,它是一种替代方法。
-volatile
在非原子字段上使用 a会产生不一致的结果。
int volatile count;
public void inc(){
count++
}
- static
将使该类的所有实例共享一个变量,但在多线程环境中仍然会产生不一致的结果。
所以当你在多线程环境中时试试这些:
1.遵循布赖恩法则总是更好:
当我们写一个变量接下来要被另一个线程读取时,或者当我们正在读取一个刚刚被另一个线程写入的变量时,它需要同步。共享字段必须设为私有,使读写方法/原子语句同步。
2.第二个选项是使用Atomic Classes
, likeAtomicInteger, AtomicLong, AtomicReference, etc.
我同意@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();
}
}
}
}
保证是原子AtomicInteger
的。
如果您用于获取先前的值,则不能保证它是原子的。 incrementAndGet()
count++
我从您的问题中遗漏了一些东西 - 并且由其他答案说明 - 静态与线程无关。
“静态”使 var 成为类级别。这意味着,如果您在一个类中定义“静态整数计数”,那么无论您为该类创建了多少个实例,所有实例都使用相同的“计数”。虽然 AtomicInteger 是一个普通类,但它只是添加了同步保护。
我认为没有保证可以看到count++
最新的值。count++
必须读取 的值count
。另一个Thread
可以写入一个新值,count
但将它的值存储在Thread
本地缓存中,即不刷新到主内存。同样,您的Thread
,读取count
,没有保证从主存储器读取,即从主存储器刷新。synchronize
保证。
AtomicInteger 就是让get和increment成为一个原子过程。它可以被认为是数据库中的排序器。它提供了增加、减少 delta int 值的实用方法。
如果您获取计数器然后处理然后更新它,静态 int 可能会导致问题。AtomicInteger 可以轻松完成,但如果您必须根据处理结果更新计数器,则无法使用它。