我有一个具有递增和递减方法的计数器类,两个方法都是同步的。
public class Counter {
int count = 0;
public synchronized void increment(){
count++;
}
public synchronized void decrement(){
count--;
}
}
从这个例子可以看出,竞争条件不会发生,只有一个线程可以访问增量或减量方法。
现在不是整数原语,如果我用Atomic Integer修改了计数器类并删除了synchronized关键字,我们可以实现同样的目的吗?
public class Counter {
AtomicInteger count = new AtomicInteger();
public void increment(){
count.incrementAndGet();
}
public void decrement(){
count.decrementAndGet();
}
}
答案 0 :(得分:6)
从java-8
开始,有一个更好(在真正争用的环境中更快阅读)选项而不是AtomicInteger
,它被称为LongAdder
并且它以相同的方式保证原子更新:
在低更新争用下,这两个类有相似之处 特征(AtomicLong)。但在高争用,预期吞吐量 这个级别要高得多,牺牲了更高的空间 消耗。
它的实施非常棒。用简单的英语(简化):如果线程之间没有争用,那就不要聪明了,只需像AtomicLong那样工作;如果有,请尝试为每个线程创建一个单独的工作空间,以便最大限度地减少争用。
它与您关注的方法相同:add
和decrement
。
答案 1 :(得分:4)
嗯,这是一个问题,你可以写一个快速的' n'要评估的脏程序:
public class ThreadExperiment {
public static class CounterSync {
int count = 0;
public synchronized void increment(){
count++;
}
public synchronized void decrement(){
count--;
}
}
public static class CounterAtomic {
AtomicInteger count = new AtomicInteger();
public void increment(){
count.incrementAndGet();
}
public void decrement(){
count.decrementAndGet();
}
}
public static void main(String[] args) throws InterruptedException {
final CounterSync counterSync = new CounterSync();
final CounterAtomic counterAtomic = new CounterAtomic();
Runnable runnable = () -> {
for (int i = 0; i < 1_000_000; i++) {
int j = i & 1;
if (j == 0) {
counterSync.increment();
counterAtomic.increment();
} else {
counterSync.decrement();
counterAtomic.decrement();
}
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(runnable);
}
for (Thread t : threads)
t.start();
for (Thread t : threads)
t.join();
System.out.println("Sync count = " + counterSync.count);
System.out.println("Atomic count = " + counterAtomic.count.intValue());
}
}
最后,两个计数器应该和将为0.通过删除&#34; synchronized&#34;关键字,你会发现事情是完全错误的。
所以是的,两者都达到了同样的目的:线程安全。
Oracle在此处记录示例: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html
对于这个简单的类,同步是一种可接受的解决方案。但对于更复杂的类,我们可能希望避免不必要的同步对活动的影响。用AtomicInteger替换int字段允许我们在不诉诸同步的情况下防止线程干扰
最后,如果您需要显式同步或原子字段足够,您的方法的行为将有所不同。