Java代码规范之使用volatile解决多线程内存可见性问题
目录
该条规范是什么
该规范指出在Java编程中,使用volatile关键字可以解决多线程内存不可见性问题。对于一写多读的情况,volatile可以保证变量的同步性。但是对于多写的情况,volatile无法解决线程安全问题。
为什么这么规定
以下是该规范的原因:
- 内存可见性问题:在多线程环境下,如果没有合适的同步机制,一个线程对共享变量的修改可能对其他线程是不可见的,即内存可见性问题。通过使用
volatile关键字可以确保变量在不同线程间的可见性。 - 原子性问题:对于一些复合操作(如
count++),单纯使用volatile关键字并不能保证线程安全。此时可以使用AtomicInteger类来保证原子性操作,或者在JDK8及以上版本中推荐使用LongAdder对象,它比AtomicLong在性能上更好(减少乐观锁的重试次数)。
多种主要用法及其代码示例
使用volatile关键字保证变量可见性
public class Example {
private volatile int count;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
使用AtomicInteger实现线程安全的自增操作
import java.util.concurrent.atomic.AtomicInteger;
public class Example {
private AtomicInteger count = new AtomicInteger();
public void increment() {
count.addAndGet(1);
}
public int getCount() {
return count.get();
}
}
使用LongAdder实现线程安全的自增操作(适用于JDK8及以上版本)
import java.util.concurrent.atomic.LongAdder;
public class Example {
private LongAdder count = new LongAdder();
public void increment() {
count.increment();
}
public long getCount() {
return count.longValue();
}
}
LongAdder 原理示例
LongAdder 是 Java 中 java.util.concurrent.atomic 包下的一个原子类,它用于高效地实现多线程环境下的长整型累加操作。相比于传统的原子类如 AtomicLong,LongAdder 在高并发场景下具有更好的性能。
原理
LongAdder 的原理是采用分段锁(Striped Locking)的方式,将计数器分成多个小计数器,每个线程对应一个小计数器。在进行累加操作时,不同线程会独立地更新各自的小计数器,最后再将所有小计数器的值求和,从而得到最终的累加结果。
这种方式可以减少并发竞争,降低了线程之间的争用,提升了并发性能。
示例
以下是一个使用 LongAdder 的示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;
public class Example {
private static final int NUM_THREADS = 10;
private static final int NUM_INCREMENTS = 100000;
public static void main(String[] args) throws InterruptedException {
LongAdder counter = new LongAdder();
ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS);
for (int i = 0; i < NUM_THREADS; i++) {
executorService.execute(() -> {
for (int j = 0; j < NUM_INCREMENTS; j++) {
counter.increment();
}
});
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("Counter: " + counter.sum());
}
}
在上述示例中,首先创建了一个 LongAdder 对象 counter。然后创建了一个固定大小的线程池,并提交了多个任务,每个任务会对计数器进行一定次数的自增操作。最后等待线程池中的任务执行完毕,并输出最终的累加结果。
通过使用 LongAdder,多个线程可以并发地对计数器进行自增操作,而无需竞争全局锁,从而提升了并发性能。
需要注意的是,LongAdder 在累加结果时使用了延迟初始化,因此在调用 sum() 方法时才会真正计算出所有小计数器的累加结果。
LongAdder 还提供了其他一些方法,如 increment(), decrement(), add(), sumThenReset() 等,可以根据具体需求选择适当的方法。
LongAdder 是在 Java 8 中引入的,用于解决高并发场景下原子累加操作的性能问题。它可以作为一个替代方案来改善原子长整型操作的性能。
本文介绍了Java中volatile关键字如何解决内存可见性问题,以及AtomicInteger和LongAdder在多线程环境下的应用,重点讲解了LongAdder的分段锁原理及其在高并发场景下的性能优势。

被折叠的 条评论
为什么被折叠?



