Нужен потокобезопасный MessageDigest в Java

Мне нужно хешировать несколько ключей из нескольких потоков, используя MessageDigest в среде, критической для производительности. Я узнал, что MessageDigest не является потокобезопасным, поскольку сохраняет свое состояние в своем объекте. Каким может быть наилучший способ добиться безопасного хэширования ключей?

Вариант использования:

MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");

//somewhere later, just need to hash a key, nothing else
messageDigest.update(key);
byte[] bytes = messageDigest.digest(); 

Конкретно:

  1. Будет ли ThreadLocal гарантированно работать? Будет ли штраф за производительность?
  2. Отличаются ли объекты, возвращаемые getInstance, и они не мешают друг другу? В документации говорится «новый» объект, но я не уверен, что это просто оболочка для (общего) общего конкретного класса?
  3. Если getInstance() возвращает «настоящие» новые объекты, целесообразно ли создавать новый экземпляр каждый раз, когда мне нужно вычислить хэш? С точки зрения штрафа за производительность - насколько это дорого?

Мой вариант использования очень прост — просто хешируйте простой ключ. Я не могу позволить себе использовать синхронизацию.

Спасибо,


person Anil Padia    schedule 09.07.2013    source источник


Ответы (3)


Создавайте новыйMessageDigest экземпляр каждый раз, когда он вам нужен.

Все экземпляры, возвращенные из getInstance(), различны. Они должны быть такими, поскольку поддерживают отдельные дайджесты (и если вам этого недостаточно, вот ссылка на источник).

ThreadLocal может обеспечить выигрыш в производительности при использовании с пулом потоков для поддержки дорогостоящих объектов. MessageDigest не особенно дорого построить (опять же, посмотрите на источник).

person parsifal    schedule 09.07.2013
comment
Если я вижу код getInstance(), кажется, что он не создает новый объект, а вызывает службу безопасности для получения объекта Object[] objs = Security.getImpl. Ниже я написал тестовый пример: 1); MessageDigest messageDigest2 = MessageDigest.getInstance(SHA-1); // обновить и дайджест и увидел, что оба объекта messageDigest разные, а также их внутренние объекты/буферы тоже разные. Итак, я думаю, ThreadLocal должен работать. И да, это веб-сервер с пулом потоков. Я буду использовать ThreadLocal. Спасибо, - person Anil Padia; 10.07.2013
comment
@AnilPadia - я настоятельно рекомендую не использовать ThreadLocal. Это преждевременная оптимизация. Я написал микро-тест, который занял примерно 2 микро-секунды, чтобы создать новый MessageDigest. Это будет намного перевешиваться кодом, использующим дайджест. - person parsifal; 10.07.2013
comment
Какие проблемы вы видите при использовании ThreadLocal. Даже если у меня есть сотни потоков, таких объектов будут сотни. Я обнаружил, что объем памяти таких объектов действительно меньше. ThreadLocal у меня работает нормально. Я также протестировал создание объектов, и это заняло 4 микросекунды. Я бы очень хотел знать, почему против ThreadLocal - person Anil Padia; 11.07.2013
comment
@AnilPadia - Как я уже сказал, создание нового MessageDigest - очень быстрая операция. Вы можете сэкономить микросекунду или две, используя ThreadLocal, но это почти наверняка бесконечно малая часть вашего общего времени выполнения. А с ThreadLocal вам нужен дополнительный код, который нужно поддерживать, и вы также должны быть более осторожны с обработкой исключений. У вас есть блок finally для очистки дайджеста при любом исключении, верно? - person parsifal; 12.07.2013
comment
Но я понимаю, что вы довольны ThreadLocal, и вряд ли повлияю на ваше мнение. Так что делайте то, что делает вас счастливым. - person parsifal; 12.07.2013
comment
Могут быть серьезные проблемы с производительностью в тяжелых многопоточных системах (я проверил это на jdk1.7.0_80), так как базовый метод java.security.Provider.getService синхронизирован и кажется одноэлементным. - person Martin Serrano; 30.10.2016
comment
Будет ли создавать экземпляр каждый раз, когда нам нужно, будет ли возникать проблема с производительностью? Я использую экземпляр MD5 - person JaskeyLam; 18.08.2017

В качестве альтернативы используйте DigestUtils, потокобезопасная оболочка Apache Commons для MessageDigest.

sha1() делает то, что вам нужно:

byte[] bytes = sha1(key)

person fishyfriend    schedule 18.12.2014
comment
Смотрите ответ ниже; DigestUtils не более потокобезопасен, чем MessageDigest, поскольку DigestUtils.getDigest() просто вызывает MessageDigest.getInstance() и преобразует проверенное исключение в непроверенное исключение. - person Siddhu; 12.10.2015
comment
Дело в том, что MessageDigest не является потокобезопасным, поэтому повторное использование одного и того же экземпляра в параллельной среде приводит к непредсказуемым результатам. использование нового/другого экземпляра (например, путем вызова MessageDigest.getInstance) каждый раз решает вашу проблему. DisgestUtils является потокобезопасным в том смысле, что каждый из его удобных методов использует новый экземпляр MessageDigest, каждый из которых вызывает (после пары вызовов) MessageDigest.getInstance, создавая новый экземпляр. например каждый вызов DigestUtils. sha256Hex (моя строка); будет использовать другой экземпляр MessageDigest - person Legna; 24.09.2016

Вы можете использовать ImmutableMessageDigest из Caesar, библиотеки с открытым исходным кодом, которую я написал.

По сути, он оборачивает экземпляр MessageDigest и клонирует его перед каждым вызовом digest() или update().

person Janez Kuhar    schedule 19.08.2020