可能出现的问题
- 线程安全问题:在高并发环境下,多个线程同时对共享的
Map
进行写入操作,可能导致数据竞争,从而出现不一致的结果。例如,两个线程同时尝试更新同一个userId
对应的交易总金额,可能会覆盖彼此的计算结果。
优化后的实现方案
- 使用ConcurrentHashMap:
ConcurrentHashMap
是线程安全的Map
实现,它允许多个线程同时进行读操作,并且对写操作进行了合理的分段锁控制,大大提高了并发性能。
代码实现
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class Transaction {
private String transactionId;
private String userId;
private double amount;
public Transaction(String transactionId, String userId, double amount) {
this.transactionId = transactionId;
this.userId = userId;
this.amount = amount;
}
public String getTransactionId() {
return transactionId;
}
public String getUserId() {
return userId;
}
public double getAmount() {
return amount;
}
}
public class TransactionStatistics {
public static void main(String[] args) throws InterruptedException {
List<Transaction> transactions = Arrays.asList(
new Transaction("T1", "U1", 100.0),
new Transaction("T2", "U2", 200.0),
new Transaction("T3", "U1", 150.0),
new Transaction("T4", "U2", 250.0)
);
Map<String, Double> userTotalAmountMap = new ConcurrentHashMap<>();
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (Transaction transaction : transactions) {
executorService.submit(() -> {
userTotalAmountMap.compute(transaction.getUserId(), (userId, totalAmount) -> {
if (totalAmount == null) {
return transaction.getAmount();
} else {
return totalAmount + transaction.getAmount();
}
});
});
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("User Total Amount Map: " + userTotalAmountMap);
}
}
并发控制原理
- 分段锁:
ConcurrentHashMap
采用了分段锁机制,它将内部的数据结构分为多个段(Segment),每个段都有自己的锁。在进行写操作(如put
、compute
等)时,只会锁住对应的段,而不是整个Map
。这样,不同的线程可以同时对不同的段进行操作,大大提高了并发性能。
- 无锁读操作:
ConcurrentHashMap
的读操作是无锁的,因为它内部使用了volatile
关键字来保证数据的可见性。当一个线程更新了某个key - value
对时,其他线程能够立即看到这个变化,从而保证了读操作的一致性。