Анализ потокобезопасности метода создания сущности в DataService

Проблема в общем изменяемом поле mdDigest: потоки могут заменить его между созданием и использованием, а MessageDigest хранит состояние. Сделайте digest локальной переменной метода.

Короткий ответ

Что ответить на собеседовании

Проблема в общем изменяемом поле mdDigest: потоки могут заменить его между созданием и использованием, а MessageDigest хранит состояние. Сделайте digest локальной переменной метода.

Подробный разбор

Ответ с пояснениями

Условие

Требуется провести ревью кода класса DataService, отвечающего за работу с объектами Data. Метод create может вызываться одновременно из разных потоков, поэтому его реализация должна быть безопасна для многопоточного доступа. При этом AccessService и DataRepository уже гарантируют корректную работу в многопоточной среде. Нужно предложить варианты улучшения реализации и указать, в чём заключается текущая проблема, если она существует.

public class DataService {
    private AccessService accessSrv;
    private DataRepository repo;
    private MessageDigest mdDigest = createDigest();

    public DataService(AccessService accessSrv, DataRepository repo) {
        this.accessSrv = accessSrv;
        this.repo = repo;
    }

    public Data get(String uid) {
        accessSrv.checkRead();
        return repo.get(uid);
    }

    public void create(String name) {
        accessSrv.checkWrite();
        mdDigest = createDigest();
        repo.save(new Data(
            HexFormat.of().formatHex(mdDigest.digest(name.getBytes())),
            name
        ));
    }

    public static MessageDigest createDigest() {
        try {
            return MessageDigest.getInstance("md5");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException();
        }
    }

    public record Data(
        String uid,
        String name
    ) {}

    public interface DataRepository {
        void save(Data data);
        Data get(String uid);
    }

    public interface AccessService {
        void checkRead();
        void checkWrite();
    }
}

Ответ

Проблема в общем изменяемом поле mdDigest: потоки могут заменить его между созданием и использованием, а MessageDigest хранит состояние. Сделайте digest локальной переменной метода.

public void create(String name) {
    accessSrv.checkWrite();
    MessageDigest digest = createDigest();
    String uid = HexFormat.of().formatHex(
            digest.digest(name.getBytes(java.nio.charset.StandardCharsets.UTF_8)));
    repo.save(new Data(uid, name));
}

Поле mdDigest удаляется, зависимости лучше объявить final. Каждый вызов теперь владеет своим digest, а кодировка задана явно. MD5 не гарантирует уникальность и не годится для защиты от умышленных коллизий; для ID обычно нужен отдельный генератор или иной согласованный контракт. При оборачивании NoSuchAlgorithmException сохраняйте cause.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее