MST

星途 面试题库

面试题:Java高并发场景下Map接口的分组统计优化

在高并发环境中,有大量的交易记录(List<Transaction>),Transaction类包含属性transactionId(交易ID)、userId(用户ID)、amount(交易金额)。需要使用Map接口对这些交易记录按userId进行分组统计每个用户的交易总金额。请分析在高并发情况下可能出现的问题,并给出优化后的实现方案及代码,同时说明涉及到的并发控制原理。
46.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

可能出现的问题

  1. 线程安全问题:在高并发环境下,多个线程同时对共享的Map进行写入操作,可能导致数据竞争,从而出现不一致的结果。例如,两个线程同时尝试更新同一个userId对应的交易总金额,可能会覆盖彼此的计算结果。

优化后的实现方案

  1. 使用ConcurrentHashMapConcurrentHashMap是线程安全的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);
    }
}

并发控制原理

  1. 分段锁ConcurrentHashMap采用了分段锁机制,它将内部的数据结构分为多个段(Segment),每个段都有自己的锁。在进行写操作(如putcompute等)时,只会锁住对应的段,而不是整个Map。这样,不同的线程可以同时对不同的段进行操作,大大提高了并发性能。
  2. 无锁读操作ConcurrentHashMap的读操作是无锁的,因为它内部使用了volatile关键字来保证数据的可见性。当一个线程更新了某个key - value对时,其他线程能够立即看到这个变化,从而保证了读操作的一致性。