在多线程环境下对字符串进行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算法进行了改进
一个简单记录java多线程下变量共享问题,分析多线程内部运行
Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结
Java 线程系列博文总结...Java线程:新特征-锁(下) Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器
java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题
Java多线程读大文件 java多线程写文件:多线程往队列中写入数据
java一个多线程的经典例子java一个多线程的经典例子
Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...
此文档能够让读者彻底了解JAVA开发中的多线程并发锁的使用
自己学着编写的一个JAVA多线程程序,该程序实现的功能是:在主线程main中创建两个子线程,A和B,线程A先运行,再运行B线程,当两个子线程都运行完毕后,才运行主线程,并最终结束整个程序的运行。 希望该程序对初学...
Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...
线程一:进行读取文件数据到线程队列,线程二:从线程队列中拉取数据进行MD5计算(抄袭了FFMPEG 播放器的 多线程解码的原理),从进度条可以看出几乎读文件线程与计算线程同步,读文件线程首先读出1个数据包 ...
单线程 单线程 单线程 单线程 单线程 单线程
一个java 多线程操作数据库应用程序!!!
java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,这时B线程再从主线程获取该变量的值,这样就实现了...
多线程启动.java多线程启动.java多线程启动.java多线程启动.java
java多线程文件传输 java多线程文件传输
java快递柜源码,涉及多线程和网络编程
详细描述了java的多线程跟锁的用法 并配多个demo