我需要在 Java 应用程序中计算 SHA-256 哈希值。 Apache HmacUtils 似乎适合于此。然而,在文档中它说“这个类是不可变的和线程安全的。但是Mac可能不是”。这里“Mac”指的是返回的实例,它实际上计算哈希值。
现在,我希望能够从多个线程计算哈希值,理想情况下它们不会互相阻塞。 实现这一目标的最佳方法是什么? 是否有参考哪种算法实际上是线程安全的? 我知道它还需要是可重入? 我应该为每个线程创建一个“Mac”实例以避免线程安全问题吗? (这样够吗?这样贵很多吗?)
有几种方法可以设置这些东西:
换句话说,您创建一个新的
java.lang.Thread
实例(或 extends Thread
的类的实例),如果您运行它,它会执行一项工作,然后结束。
这是一个坏主意 - 至少,如果速度很重要并且您有大量工作要做。制作这些线程的实际成本取决于大量因素,真正的组合爆炸:JVM 版本、供应商、底层操作系统、底层架构。从速度角度来看,这可能是一个很好的方法,但是在某些组合中,这比替代方案慢得多,替代方案是拥有一组有限的线程(大约等于 CPU 中的核心数量,也许大约是 2 倍,但是与核心数量线性相关),并且这些线程将作业从 em 的中央队列中拉出,直到所有作业完成。
有各种框架可以让这个变得“好”(例如 fork/join、ExecutorPool 等),如果您想以某种方式处理所有这些,您可以在java.util.concurrent
中使用集合类型作为“所有作业的中央队列”要做的就是所有线程都从'.拉出 重点是,担心为每个作业创建 Mac 对象的新实例所涉及的成本(实际上,为了避免任何线程安全问题,您最终必须要做的事情)有点像如果你没有解决房间里的大象问题,那就太愚蠢了。换句话说,
如果这描述了您当前的设置,请先修复它,这样您最终会:
你有工作,还有一群执行者来完成工作。然后,相对于作业计数,您拥有的线程数量是恒定的,即可以忽略。
解决方案就是简单地为每个线程创建 1 个 Mac 对象。这意味着每个 CPU 有 1 个 mac 对象,这是微不足道的、可以忽略的成本。尝试使用更少的 mac 对象是没有意义的;即使这本来就很好(即 mac 对象根本没有上下文,这显然不是真的),你也无法获得可测量的性能,或者如果不知何故这些 mac 对象
是线程安全但确实有状态(因为 JVM 非常擅长消除不相关的同步成本,因为该对象永远不会从多个线程中使用,即使如果这样做的话它会成功)。
实现这一目标的一个简单工具就是拥有一个ThreadLocal
:
private static final ThreadLocal<Mac> MACS = new ThreadLocal<Mac>() {
@Override public Mac initialValue() {
return HmacUtils.getInitializedMac("algorithm", key);
}
};
public void codeThatIsRunInManyThreads() {
Mac mac = MACS.get();
mac.reset();
// use mac here. Rest safely knowing it's all safe now.
}