单体服务,多线程共享计数器,不使用分布式锁保证线程安全。
对于多线程环境下的共享计数器,选择AtomicLong还是更好的选择,因为它能保证线程安全且具有更好的性能特性。
详细分析
1. 线程安全比较
AtomicLong:
- 使用 CAS(Compare and Swap)操作保证原子性
- 提供完整的原子操作 API(getAndAdd、addAndGet 等)
- 完全线程安全
LongAdder:
- 本质上是 AtomicLong 的优化版本
- 内部使用多个 Cell 分段存储,减少竞争
- 同样线程安全
2. 性能对比
java
运行
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class CounterPerformanceTest {
private static final int THREADS = 16;
private static final int OPERATIONS = 1000000;
public static void main(String[] args) throws InterruptedException {
// 测试AtomicLong
long atomicTime = testCounter(new AtomicLongCounter());
System.out.println("AtomicLong: " + atomicTime + " ms");
// 测试LongAdder
long adderTime = testCounter(new LongAdderCounter());
System.out.println("LongAdder: " + adderTime + " ms");
}
private static long testCounter(Counter counter) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(THREADS);
long startTime = System.currentTimeMillis();
for (int i = 0; i < THREADS; i++) {
executor.submit(() -> {
for (int j = 0; j < OPERATIONS; j++) {
counter.add(1);
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.println("Final count: " + counter.get());
return endTime - startTime;
}
interface Counter {
void add(long delta);
long get();
}
static class AtomicLongCounter implements Counter {
private final AtomicLong count = new AtomicLong(0);
@Override
public void add(long delta) {
count.addAndGet(delta);
}
@Override
public long get() {
return count.get();
}
}
static class LongAdderCounter implements Counter {
private final LongAdder count = new LongAdder();
@Override
public void add(long delta) {
count.add(delta);
}
@Override
public long get() {
return count.sum();
}
}
}
3. 使用场景分析
AtomicLong 适合:
- 中等并发场景
- 需要精确计数的场景
- 需要完整原子操作 API 的场景
LongAdder 适合:
- 高并发场景(大量线程同时更新)
- 写操作远多于读操作的场景
- 对实时性要求不高的场景
4. 额度计数器实现示例
java
运行
import java.util.concurrent.atomic.AtomicLong;
public class SharedQuotaCounter {
private final AtomicLong quota;
private final long maxQuota;
public SharedQuotaCounter(long initialQuota, long maxQuota) {
this.quota = new AtomicLong(initialQuota);
this.maxQuota = maxQuota;
}
/**
* 尝试增加额度
* @param amount 要增加的额度
* @return 是否成功(是否超过超过最大额度)
*/
public synchronized
public boolean tryAddQuota(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
long current;
long newValue;
do {
current = quota.get();
newValue = current + amount;
// 检查超过最大额度
if (newValue > maxQuota) {
return false;
}
// 使用CAS操作更新
} while (!quota.compareAndSet(current, newValue));
return true;
}
/**
* 增加额度(不检查最大限制)
* @param amount 要增加的额度
*/
public void addQuotaUnchecked(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
quota.addAndGet(amount);
}
/**
* 获取当前额度
*/
public long getCurrentQuota() {
return quota.get();
}
/**
* 重置额度
*/
public void resetQuota(long newQuota) {
quota.set(newQuota);
}
/**
* 减少额度
*/
public void subtractQuota(long amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
long current;
long newValue;
do {
current = quota.get();
newValue = current - amount;
// 防止出现负数
if (newValue < 0) {
newValue = 0;
}
} while (!quota.compareAndSet(current, newValue));
}
public long getMaxQuota() {
return maxQuota;
}
public boolean hasRemainingQuota() {
return quota.get() < maxQuota;
}
}
5. 使用示例
java
运行
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException {
// 创建额度计数器,初始额度0,最大额度1000
SharedQuotaCounter counter = new SharedQuotaCounter(0, 1000);
ExecutorService executor = Executors.newFixedThreadPool(8);
// 启动多个线程增加额度
for (int i = 0; i < 8; i++) {
final int threadId = i;
executor.submit(() -> {
for (int j = 0; j < 200; j++) {
long amount = ThreadLocalRandom.current().nextLong(1, 10);
boolean success = counter.tryAddQuota(amount);
if (success) {
System.out.printf("Thread %d: Added %d, Current: %d%n",
threadId, amount, counter.getCurrentQuota());
} else {
System.out.printf("Thread %d: Failed to add %d, Max reached%n",
threadId, amount);
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("Final quota: " + counter.getCurrentQuota());
System.out.println("Max quota: " + counter.getMaxQuota());
}
}
结论
推荐使用 AtomicLong,原因:
- 线程安全保证:完整的原子操作支持
- 性能优秀:在大多数并发场景下表现良好
- API 丰富:提供各种原子操作方法
- 内存一致性:保证 volatile 语义
只有在以下情况下考虑 LongAdder:
- 极高并发(数十个以上线程同时更新)
- 写操作远多于读操作
- 对实时性要求不高
对于额度计数器这种需要精确控制和频繁读取的场景,AtomicLong 是更稳妥的选择。