多线程环境中的Java CRC32计算
我有以下用于crc32计算的util类:多线程环境中的Java CRC32计算,java,multithreading,crc32,Java,Multithreading,Crc32,我有以下用于crc32计算的util类: 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(); checks
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();
}
}
对我来说,表现是一个非常重要的标准
现在我正在考虑对这个方法进行重构,我正在考虑将校验和作为一个静态字段移动到类级别。。。大概是这样的:
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();
}
}
但我不确定它是否能在并发多线程环境中正常工作。请建议-这种重构是不是一个好主意。不,您的代码不是线程安全的。幸运的是,您可以通过一个简单的类使其成为线程安全的,并且几乎不会有任何性能损失:
这是你的答案 不,您的代码不是线程安全的。幸运的是,您可以通过一个简单的类使其成为线程安全的,并且几乎不会有任何性能损失:
这是你的答案 显然,您不能在多线程环境中这样做,因为CRC32类不是线程安全的 简短回答:它不是线程安全的,因为它的javadoc不包含这个提示 更详细:若你们打开CRC32类的源代码,你们会看到,这个类不包含任何同步块,它不是原子的,并且包含对象变量
private int crc;
这是不同步的
UPD:但您可以使用ThreadLocal作为@Dariusz。显然,您不能在多线程环境中这样做,因为CRC32类不是线程安全的 简短回答:它不是线程安全的,因为它的javadoc不包含这个提示 更详细:若你们打开CRC32类的源代码,你们会看到,这个类不包含任何同步块,它不是原子的,并且包含对象变量
private int crc;
这是不同步的
UPD:但您可以将ThreadLocal用作@Dariusz。正如其他人所说,CRC32不是线程安全的,因此您必须同步或使用ThreadLocal,但这些都不太可能有帮助 如果您查看的实现,is有一个字段。在做任何事情之前,先对代码进行基准测试。在Java复杂的GC、JIT和escape分析之间,很难预测您是否会看到任何好处 重写此代码以避免数组分配可能会给您带来更大的好处:
byte[] bytes = input.getBytes();
编辑:请不要这样做,除非你必须这样做
这将展开字符串的内部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来说是微不足道的。使性能复杂化的是平衡将字节复制到缓冲区中,只是为了通过对实际CRC32实现的JNI调用读取它们。由于JNI开销,中间缓冲区实际上可能更快。在做这件事之前一定要仔细阅读;如果您实际上没有重用缓冲区,这可能会很慢
当你真正深入研究正在发生的事情时,你会发现getBytes比你意识到的要复杂得多,而且担心一个微不足道的、短暂的CRC32对象的分配并不是性能的主要因素。正如其他人所说,CRC32不是线程安全的,所以你必须同步或使用ThreadLocal,但这些都不太可能有帮助 如果您查看的实现,is有一个字段。在做任何事情之前,先对代码进行基准测试。在Java复杂的GC、JIT和escape分析之间,很难预测您是否会看到任何好处 重写此代码以避免数组分配可能会给您带来更大的好处:
byte[] bytes = input.getBytes();
编辑:请不要这样做,除非你必须这样做
这将展开字符串的内部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来说是微不足道的。使性能复杂化的是平衡将字节复制到缓冲区中,只是为了通过对实际CRC32实现的JNI调用读取它们。由于JNI开销,中间缓冲区实际上可能更快。在做这件事之前一定要仔细阅读;如果您实际上没有重用缓冲区,这可能会很慢
当您真正深入研究正在发生的事情时,您会发现getBytes比您意识到的要复杂得多,并且担心一个微不足道的、短期存在的CRC32对象的分配并不是影响性能的主要因素。您不应该这样做,因为即使忽略多线程,getValue不会重置以前调用CRC32时传入的CRC32数据。您需要在crc32方法的开头或结尾使用该方法。您不应该这样做,因为即使忽略多线程,getValue也不会重置在以前调用crc32期间传入的crc32数据。您需要在crc32方法的开头或结尾使用该方法。现在,您使用的是默认编码。你能安全地假设ASCII吗?现在,你正在使用默认编码。你安全吗 假设ASCII?