Сервис: надёжное создание и извлечение записей

Новый MessageDigest записывается в общее поле, поэтому два create могут использовать один экземпляр после гонки присваиваний. Создание объекта на каждый вызов помогает только при локальной переменной.

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

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

Новый MessageDigest записывается в общее поле, поэтому два create могут использовать один экземпляр после гонки присваиваний. Создание объекта на каждый вызов помогает только при локальной переменной.

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

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

Условие

Проведение ревью кода на предмет обеспечения потокобезопасности и повторного использования объекта MessageDigest внутри класса DataService.

public class DataService {
    private AccessService access;
    private DataRepository repository;
    private MessageDigest digest = get();

    public DataService(AccessService access, DataRepository repository) {
        this.access = access;
        this.repository = repository;
    }

    public Data get(String uid) {
        access.checkRead();
        return repository.get(uid);
    }

    public void create(String name) {
        access.checkWrite();
        digest = get();
        repository.save(new Data(
            HexFormat.of().formatHex(digest.digest(name.getBytes())),
            name
        ));
    }

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

    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();
    }
}

Ответ

Новый MessageDigest записывается в общее поле, поэтому два create могут использовать один экземпляр после гонки присваиваний. Создание объекта на каждый вызов помогает только при локальной переменной.

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

Общее поле digest нужно удалить. Можно синхронизировать доступ или применять ThreadLocal, но локальный экземпляр проще для начала. Access и repository требуют собственной гарантии потокобезопасности. MD5 допускает коллизии и не является надёжным уникальным идентификатором по произвольному имени; это отдельная проблема модели данных, не только потоков.

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

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

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

Подробнее