多线程环境中的Java CRC32计算

多线程环境中的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

我有以下用于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();
        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?