2017-02-12 2 views
1

У меня есть следующий класс Util для crc32 расчета:расчет Java CRC32 в многопоточной envirinment

import java.util.zip.CRC32; 
import java.util.zip.Checksum; 

public class StringUtils { 

    public static long crc32(String input) { 
     byte[] bytes = input.getBytes(); 
     Checksum checksum = new CRC32(); 
     checksum.update(bytes, 0, bytes.length); 

     return checksum.getValue(); 
    } 

} 

Производительность является очень важным критерием для меня.

Прямо сейчас я думаю о возможном рефакторинга этого метода, и я думаю, чтобы переместить checksum на уровне класса, как статическое поле ... что-то вроде этого:

public class StringUtils { 

    public static Checksum checksum = new CRC32(); 

    public static long crc32(String input) { 
     byte[] bytes = input.getBytes(); 
     checksum.update(bytes, 0, bytes.length); 

     return checksum.getValue(); 
    } 

} 

, но я m не уверен, что он будет корректно работать в параллельной многопоточной среде. Пожалуйста, посоветуйте - такой рефакторинг - хорошая идея или нет.

+1

Вы не должны этого делать, потому что, даже игнорируя многопоточность, 'getValue' не сбрасывает данные CRC32, загруженные во время предыдущих вызовов на' crc32'. Вам нужно будет использовать метод ['reset'] (https://docs.oracle.com/javase/8/docs/api/java/util/zip/CRC32.html#reset--) либо в начале или конец метода 'crc32'. –

ответ

1

Как и другие пользователи, CRC32 не является потокобезопасным, поэтому вам придется либо синхронизировать, либо использовать ThreadLocal, но это вряд ли поможет.

Если вы посмотрите, как CRC32, есть Поле. Прежде чем что-либо сделать, сравните свой код. Между сложным GC, JIT и анализом побега Java трудно предсказать, если вы увидите какую-либо выгоду.

Переписывая это, чтобы избежать выделения массива может дать вам большую выгоду:

byte[] bytes = input.getBytes(); 

Edit: пожалуйста, не делайте этого, если вы абсолютно не должны.

Это разворачивает струны внутреннего getBytes() избежать некоторую промежуточную буферизацию и использует CRC32, имеющий оптимизацию прямых буферов байт:

public class StringUtils { 
    private static final ThreadLocal<ByteBuffer> BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocateDirect(4094)); 

    public static long crc32(String input) { 
     CharBuffer inputBuffer = CharBuffer.wrap(input); 
     ByteBuffer buffer = BUFFER.get(); 
     CRC32 crc32 = new CRC32(); 
     CharsetEncoder encoder = Charset.defaultCharset().newEncoder(); 

     CoderResult coderResult; 
     do { 
      try { 
       coderResult = encoder.encode(inputBuffer, buffer, true); 
       buffer.flip(); 
       crc32.update(buffer); 
      } finally { 
       buffer.reset(); 
      } 
     } while (coderResult.isOverflow()); 

     return crc32.getValue(); 
    } 
} 

Вы могли бы быть в состоянии сделать даже лучше вручную делать кодировку (которая тривиально для ASCII). То, что усложняет производительность, - это балансировка копирования байтов в буфер только для их считывания с помощью вызова JNI для реальной реализации CRC32. Промежуточный буфер может быть быстрее из-за накладных расходов JNI. Обязательно прочитайте direct bytebuffers перед этим; если вы на самом деле не используете буфер повторно, это может быть медленным.

Когда вы на самом деле разбираетесь в том, что происходит, вы обнаружите, что getBytes() намного сложнее, чем вы понимаете, и беспокоиться о распределении тривиального, недолговечного объекта CRC32 не является основным фактором производительности.

+0

Не могли бы вы показать, как это можно переписать? – alexanoid

+0

Прямо сейчас вы используете кодировку по умолчанию. Можете ли вы спокойно принять ASCII? –

4

Нет, ваш код не является потокобезопасным. К счастью, вы можете сделать его потокобезопасным с одним простым классом и вряд ли с потерей производительности:

ThreadLocal<Checksum> - ваш ответ.

3

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

Короткий ответ: он не является потокобезопасным, потому что его javadoc не содержит этого намека.

Более подробно: если вы будете с открытым исходным кодом CRC32 класса, вы увидите, что этот класс не содержит каких-либо блоков синхронизации, это не атомная, и содержат переменную

private int crc; 

объект, который не является синхронизированы.

UPD: Но вы можете использовать ThreadLocal<Checksum> в @Dariusz suggested in his answer.

Смежные вопросы