在多线程环境下对字符串进行MD5,到底应该使用加锁来共享同一个MessageDigest呢?还是每次新创建一个,个人认为需要 根据程序运行的环境来分别对待。下边是从org.springframework.extensions.surf摘取的一段代码,实现了两种调用方式, 不过到底在何种情况下使用何种方式,目前还不是很清晰,希望通过测试能够得出结论。
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * The MD5 utility class computes the MD5 digest (aka: "hash") of a block * of data; an MD5 digest is a 32-char ASCII string. * * The synchronized/static function "Digest" is useful for situations where * lock contention in the application is not expected to be an issue. * * The unsynchronized/non-static method "digest" is useful in a * multi-threaded program that wanted to avoid locking by creating * an MD5 object for exclusive use by a single thread. * * * * EXAMPLE 1: Static usage * * import org.springframework.extensions.surf.util.MD5; * String x = MD5.Digest("hello".getBytes()); * * * EXAMPLE 2: Per-thread non-static usage * * import org.springframework.extensions.surf.util.MD5; * MD5 md5 = new MD5(); * ... * String x = md5.digest("hello".getBytes()); * * * Email: diwayou@163.com * User: diwayou * Date: 13-4-15 * Time: 下午11:18 */ public class MD5 { private static final byte[] ToHex_ = { '0','1','2','3','4','5','6','7', '8','9','a','b','c','d','e','f' }; private MessageDigest md5_ = null; static private MessageDigest Md5_; static { try { Md5_ = MessageDigest.getInstance("MD5");} // MD5 is supported catch ( NoSuchAlgorithmException e ) {}; // safe to swallow }; /** * Constructor for use with the unsynchronized/non-static method * "digest" method. Note that the "digest" function is not * thread-safe, so if you want to use it, every thread must create * its own MD5 instance. If you don't want to bother & are willing * to deal with the potential for lock contention, use the synchronized * static "Digest" function instead of creating an instance via this * constructor. */ public MD5() { try { md5_ = MessageDigest.getInstance("MD5");} // MD5 is supported catch ( NoSuchAlgorithmException e ) {}; // safe to swallow } /** * Thread-safe static digest (hashing) function. * * If you want to avoid lock contention, create an instance of MD5 * per-thead, anc call the unsynchronized method 'digest' instead. */ public static synchronized String Digest(byte[] dataToHash) { Md5_.update(dataToHash, 0, dataToHash.length); return HexStringFromBytes( Md5_.digest() ); } /** * Non-threadsafe MD5 digest (hashing) function */ public String digest(byte[] dataToHash) { md5_.update(dataToHash, 0, dataToHash.length); return HexStringFromBytes( md5_.digest() ); } private static String HexStringFromBytes(byte[] b) { byte [] hex_bytes = new byte[ b.length * 2 ]; int i,j=0; for (i=0; i < b.length; i++) { hex_bytes[j] = ToHex_[ ( b[i] & 0x000000F0 ) >> 4 ] ; hex_bytes[j+1] = ToHex_[ b[i] & 0x0000000F ]; j+=2; } return new String( hex_bytes ); } }
下边是闲暇时写的一段测试代码,测试方式考虑因素还是比较少的,只考虑到了竞争线程的数量,代码如下:
package com.diwayou.logq; import com.diwayou.logq.util.MD5; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartFrame; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.XYDataset; import java.io.UnsupportedEncodingException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Email: diwayou@163.com * User: diwayou * Date: 13-3-26 * Time: 下午5:48 */ public class LogQ { public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException { String s = "alibabagogogo"; final byte[] message = s.getBytes("GBK"); int coreNum = Runtime.getRuntime().availableProcessors(); int scale = 100; System.out.println(String.format("Core size is %d", coreNum)); int TEST_TIMES = 1; long startTime, endTime; DefaultXYDataset xyDataset = new DefaultXYDataset(); double[][] elapse = new double[2][scale]; for (int j = 0; j < scale; j++) { ExecutorService executorService = Executors.newFixedThreadPool(scale); startTime = System.nanoTime(); for (int i = 0; i < TEST_TIMES; i++) { executorService.submit(new Runnable() { @Override public void run() { MD5.Digest(message); } }); } executorService.shutdown(); endTime = System.nanoTime(); System.out.println(String.format("Pool size %d, elapse time %d", j, (endTime - startTime))); elapse[0][j] = j; if (j == 0) { elapse[1][j] = 0; } else { elapse[1][j] = endTime - startTime; } } xyDataset.addSeries("Synchronized", elapse); elapse = new double[2][scale]; for (int j = 0; j < scale; j++) { ExecutorService executorService = Executors.newFixedThreadPool(scale); startTime = System.nanoTime(); for (int i = 0; i < TEST_TIMES; i++) { executorService.submit(new Runnable() { @Override public void run() { MD5 md5 = new MD5(); md5.digest(message); } }); } executorService.shutdown(); endTime = System.nanoTime(); System.out.println(String.format("Pool size %d, elapse time %d", j, (endTime - startTime))); elapse[0][j] = j; if (j == 0) { elapse[1][j] = 0; } else { elapse[1][j] = endTime - startTime; } } xyDataset.addSeries("NewEveryTime", elapse); displayChart("Synchronized VS NewEveryTime", xyDataset); } private static void displayChart(String title, XYDataset dataset) { JFreeChart xyLineChart = ChartFactory.createXYLineChart(title, "Pool Size", "Elapse Time", dataset, PlotOrientation.VERTICAL, true, true, true); ChartFrame chartFrame = new ChartFrame("Stat Result", xyLineChart); chartFrame.pack(); chartFrame.setVisible(true); } }
运行结果如下:
(1)第一次

Core size is 8 Pool size 0, elapse time 2012874 Pool size 1, elapse time 839776 .............. (2)第二次

Core size is 8 Pool size 0, elapse time 2256508 Pool size 1, elapse time 795155 Pool size 2, elapse time 968285 ................... 结论:通过测试结果,可以看出每次都new一个新的并不比共享同一个MessageDigest慢,而且不需要锁,这样在服务器高并发的环境下,就不会出现共享锁性能瓶颈 的问题,这样可以减少由于共享锁出现的上下文切换,个人倾向于每次都new一个。 本人菜鸟一个,分析有误还请大家支出,随便批评,这样我才能进步。
相关推荐
总的来说,这个压缩包提供了一个支持多线程的MD5加密算法源代码实现,这对于处理大量数据的系统或者需要高效计算MD5值的场景非常有用。然而,需要注意的是,由于MD5的安全性问题,不推荐用于密码存储等需要高度安全...
“ThreadLocal.md”是关于Java中的ThreadLocal类,它为每个线程提供了一个独立的变量副本,避免了线程间的数据共享问题,从而简化了多线程编程。然而,如果不正确地使用ThreadLocal,可能会导致内存泄漏,这正是...
一、Java多线程 1. **线程的创建**:Java提供了两种方式创建线程,分别是通过实现Runnable接口和继承Thread类。前者可以避免单继承的限制,更利于代码复用;后者则直接继承Thread,可以直接调用其run()方法。 2. *...
通过合理运用`synchronized`、`ReentrantLock`、`volatile`以及Java并发包中的其他工具,开发者可以有效地解决多线程环境下的数据一致性和线程同步问题。然而,理解和应用这些工具仍然需要深入的理论知识和实践经验...
在IT领域,多线程是程序设计中的一个重要概念,尤其在现代高性能计算和并发处理中。这个名为"多线程02"的压缩包显然包含了关于多线程编程的深入学习资料,包括MD文档和代码示例。让我们深入探讨一下多线程的相关知识...
- 在多核CPU环境下,多线程能够充分利用硬件资源,实现真正的并行计算,显著提升程序性能。 - **弊**: - 并发编程复杂度较高,需要处理好线程间的同步问题,否则容易出现死锁、资源闲置、内存泄露等问题。 - 调试...
线程安全是指在多线程环境下,当多个线程同时访问同一个类的实例时,即使没有外部同步机制(如synchronized关键字),该类也能够保证数据的一致性。简单来说,线程安全的类能够在多线程环境中正常工作而不出现数据...
在IT行业中,多线程是程序设计中的一个重要概念,尤其在服务器端开发、并发处理以及高性能计算中扮演着核心角色。本资源包“多线程01”包含了关于多线程的详细讲解,包括Markdown文档和配套代码示例,旨在帮助初学者...
4. **错误处理与异常传播**:在多线程环境下,错误处理尤为重要,因为一个线程的异常可能会影响其他线程。确保每个线程都有适当的异常处理机制,并考虑如何将这些异常信息传递给主线程进行处理。 5. **线程池管理**...
3. **并发工具类**:Java util.concurrent包提供了丰富的并发工具,如Semaphore(信号量)用于限制同时访问特定资源的线程数量,CountDownLatch用于一次性释放多个等待线程,CyclicBarrier用于同步多线程到达某个点...
例如,`BlockingQueue`可以使一个线程在队列为空时等待,直到另一个线程将元素放入队列,这在多线程数据共享和传递中非常有效。 总的来说,回调在Java线程间通信中起到桥梁的作用,使得线程能够以非阻塞的方式互相...
Java多线程是Java编程中不可或缺的部分,它允许程序同时执行多个任务,提高了程序的效率和响应速度。本文主要探讨了多线程的概念、应用场景以及Java中创建线程的两种方式。 首先,进程和线程是理解多线程的基础概念...
根据提供的文件信息,可以看出这份资料主要关注的是Java中与多线程相关的高级同步机制,特别是CAS(Compare and Swap)操作、自旋锁以及Unsafe类的使用。下面将详细阐述这些概念及其应用场景。 ### CAS (Compare ...
为了解决多线程并发访问共享资源时可能出现的问题,Java提供了`synchronized`关键字和`java.util.concurrent.locks`包中的`Lock`接口,以实现线程同步控制。 ### 使用synchronized关键字进行同步 `synchronized`...
在Java编程中,保证多线程环境下的数据一致性和操作的正确性是至关重要的。为了实现这一目标,Java提供了多种锁机制,其中最常用的是`synchronized`关键字和`java.util.concurrent.locks`包中的锁类。接下来,我们将...
多线程环境下共享资源可能会导致数据不一致和其他并发问题。使用同步机制,比如synchronized关键字,或者显式的锁(如java.util.concurrent.locks.Lock),以及并发集合(如ConcurrentHashMap),都是保证线程安全的...
多线程基础 ##### 14.1 了解进程和线程 - 进程是资源分配的基本单位。 - 线程是CPU调度的基本单位。 ##### 14.2 Java线程实现 - 实现`Runnable`接口。 - 继承`Thread`类。 ##### 14.3 Java线程状态 - 新建:`NEW...
在Java编程领域,无锁编程是一种高级的并发控制技术,旨在提高多线程环境下的程序性能和可伸缩性。本笔记将深入探讨无锁编程的概念、原理以及在Java中的实现方式。 首先,理解无锁编程的核心思想是关键。无锁编程,...
在Java中,实现多线程的方式主要有两种,一种是继承Thread类,另一种是实现Runnable接口。这两种方式都可以用于解决多线程编程中遇到的问题。 继承Thread类的方式,是在自定义类中重写run()方法。通过创建该类的...