MST
星途 面试题库

面试题:Java类设计与重构中的性能优化及线程安全

在高并发场景下,有一个Java类负责处理用户登录认证,其中涉及到数据库查询、密码验证、生成令牌等操作。随着用户量的增加,性能出现瓶颈且存在线程安全问题。请详细说明如何从类设计和重构的角度进行性能优化并确保线程安全,包括但不限于合理选择数据结构、使用线程池、锁机制优化等方面,同时给出关键代码示例。
12.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 类设计优化

  • 单一职责原则:将不同功能拆分到不同类中,如数据库查询操作封装到 UserDao 类,密码验证封装到 PasswordVerifier 类,令牌生成封装到 TokenGenerator 类。这样每个类职责明确,便于维护和复用。

2. 数据结构选择

  • 缓存用户信息:使用 ConcurrentHashMap 缓存已登录用户信息,减少数据库查询次数。ConcurrentHashMap 线程安全,适用于高并发场景。
private static final ConcurrentHashMap<String, User> userCache = new ConcurrentHashMap<>();

3. 线程池使用

  • 创建线程池:使用 ThreadPoolExecutor 创建线程池来处理用户登录认证任务,提高线程复用,减少线程创建销毁开销。
private static final ExecutorService executorService = new ThreadPoolExecutor(
    10,
    100,
    60L,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000)
);
  • 提交任务:在登录认证方法中,将任务提交到线程池。
public void authenticateUser(User user) {
    executorService.submit(() -> {
        // 具体认证逻辑
    });
}

4. 锁机制优化

  • 细粒度锁:对于涉及共享资源的操作,使用 ReentrantLock 并采用细粒度锁策略。例如,在更新用户缓存时,只对缓存操作加锁。
private static final ReentrantLock lock = new ReentrantLock();
public void updateUserCache(User user) {
    lock.lock();
    try {
        userCache.put(user.getUsername(), user);
    } finally {
        lock.unlock();
    }
}
  • 读写锁:如果对用户缓存读操作远多于写操作,可以使用 ReentrantReadWriteLock。读操作时允许多个线程同时进行,写操作时只允许一个线程进行。
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static final Lock readLock = readWriteLock.readLock();
private static final Lock writeLock = readWriteLock.writeLock();
public User getUserFromCache(String username) {
    readLock.lock();
    try {
        return userCache.get(username);
    } finally {
        readLock.unlock();
    }
}
public void updateUserCache(User user) {
    writeLock.lock();
    try {
        userCache.put(user.getUsername(), user);
    } finally {
        writeLock.unlock();
    }
}

5. 关键代码示例

下面是一个简化的用户登录认证类示例:

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class UserAuthenticator {
    private static final ConcurrentHashMap<String, User> userCache = new ConcurrentHashMap<>();
    private static final ExecutorService executorService = new ThreadPoolExecutor(
        10,
        100,
        60L,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(1000)
    );
    private static final ReentrantLock lock = new ReentrantLock();

    public void authenticateUser(User user) {
        executorService.submit(() -> {
            boolean isValid = PasswordVerifier.verifyPassword(user.getPassword());
            if (isValid) {
                User dbUser = UserDao.getUserFromDB(user.getUsername());
                if (dbUser != null) {
                    String token = TokenGenerator.generateToken(dbUser);
                    lock.lock();
                    try {
                        userCache.put(dbUser.getUsername(), dbUser);
                    } finally {
                        lock.unlock();
                    }
                    System.out.println("User authenticated, token: " + token);
                }
            }
        });
    }
}

class UserDao {
    public User getUserFromDB(String username) {
        // 实际数据库查询逻辑
        return null;
    }
}

class PasswordVerifier {
    public static boolean verifyPassword(String password) {
        // 实际密码验证逻辑
        return true;
    }
}

class TokenGenerator {
    public static String generateToken(User user) {
        // 实际令牌生成逻辑
        return "token";
    }
}

class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}