Проблема в общем изменяемом поле mdDigest: потоки могут заменить его между созданием и использованием, а MessageDigest хранит состояние. Сделайте digest локальной переменной метода.
Анализ потокобезопасности метода создания сущности в DataService
Проблема в общем изменяемом поле 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.