java MD5 多线程环境下共享锁还是每次创建一个新的MessageDigest

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

在多线程环境下对字符串进行MD5,到底应该使用加锁来共享同一个MessageDigest呢?还是每次新创建一个,个人认为需要 根据程序运行的环境来分别对待。下边是从org.springframework.extensions.surf摘取的一段代码,实现了两种调用方式, 不过到底在何种情况下使用何种方式,目前还不是很清晰,希望通过测试能够得出结论。

Java代码   收藏代码
  1. import java.security.MessageDigest;  
  2. import java.security.NoSuchAlgorithmException;  
  3.   
  4. /** 
  5.  *  The MD5 utility class computes the MD5 digest (aka: "hash") of a block 
  6.  *  of data; an MD5 digest is a 32-char ASCII string. 
  7.  * 
  8.  *  The synchronized/static function "Digest" is useful for situations where 
  9.  *  lock contention in the application is not expected to be an issue. 
  10.  * 
  11.  *  The unsynchronized/non-static method "digest" is useful in a 
  12.  *  multi-threaded program that wanted to avoid locking by creating 
  13.  *  an MD5 object for exclusive use by a single thread. 
  14.  * 
  15.  * 
  16.  *  
  17.  *  EXAMPLE 1:  Static usage 
  18.  * 
  19.  *      import org.springframework.extensions.surf.util.MD5; 
  20.  *      String x = MD5.Digest("hello".getBytes()); 
  21.  * 
  22.  * 
  23.  *  EXAMPLE 2:  Per-thread non-static usage 
  24.  * 
  25.  *      import org.springframework.extensions.surf.util.MD5; 
  26.  *      MD5 md5 = new MD5(); 
  27.  *      ... 
  28.  *      String x = md5.digest("hello".getBytes()); 
  29.  * 
  30.  *  
  31.  
  32. * Email: [email protected] 
  33.  * User: diwayou 
  34.  * Date: 13-4-15 
  35.  * Time: 下午11:18 
  36.  */  
  37.   
  38. public class MD5 {  
  39.     private static final byte[] ToHex_ =  
  40.             { '0','1','2','3','4','5','6','7',  
  41.                     '8','9','a','b','c','d','e','f'  
  42.             };  
  43.   
  44.     private MessageDigest md5_ = null;  
  45.   
  46.     static private MessageDigest  Md5_;  
  47.     static  
  48.     {  
  49.         try { Md5_ = MessageDigest.getInstance("MD5");}  // MD5 is supported  
  50.         catch ( NoSuchAlgorithmException e ) {};         // safe to swallow  
  51.     };  
  52.   
  53.     /** 
  54.      *  Constructor for use with the unsynchronized/non-static method 
  55.      *  "digest" method.   Note that the "digest" function is not 
  56.      *  thread-safe, so if you want to use it, every thread must create 
  57.      *  its own MD5 instance.   If you don't want to bother & are willing 
  58.      *  to deal with the potential for lock contention, use the synchronized 
  59.      *  static "Digest" function instead of creating an instance via this 
  60.      *  constructor. 
  61.      */  
  62.     public MD5()  
  63.     {  
  64.         try { md5_ = MessageDigest.getInstance("MD5");}  // MD5 is supported  
  65.         catch ( NoSuchAlgorithmException e ) {};         // safe to swallow  
  66.     }  
  67.   
  68.     /** 
  69.      *   Thread-safe static digest (hashing) function. 
  70.      * 
  71.      *   If you want to avoid lock contention, create an instance of MD5 
  72.      *   per-thead, anc call the unsynchronized method 'digest' instead. 
  73.      */  
  74.     public static synchronized String Digest(byte[] dataToHash)  
  75.     {  
  76.         Md5_.update(dataToHash, 0, dataToHash.length);  
  77.         return HexStringFromBytes( Md5_.digest() );  
  78.     }  
  79.   
  80.     /** 
  81.      *  Non-threadsafe MD5 digest (hashing) function 
  82.      */  
  83.     public String digest(byte[] dataToHash)  
  84.     {  
  85.         md5_.update(dataToHash, 0, dataToHash.length);  
  86.         return HexStringFromBytes( md5_.digest() );  
  87.     }  
  88.   
  89.     private static String HexStringFromBytes(byte[] b)  
  90.     {  
  91.         byte [] hex_bytes = new byte[  b.length * 2 ];  
  92.         int i,j=0;  
  93.   
  94.         for (i=0; i < b.length; i++)  
  95.         {  
  96.             hex_bytes[j]   = ToHex_[ ( b[i] & 0x000000F0 ) >> 4 ] ;  
  97.             hex_bytes[j+1] = ToHex_[   b[i] & 0x0000000F ];  
  98.             j+=2;  
  99.         }  
  100.         return new String( hex_bytes );  
  101.     }  
  102. }  

 下边是闲暇时写的一段测试代码,测试方式考虑因素还是比较少的,只考虑到了竞争线程的数量,代码如下:

Java代码   收藏代码
  1. package com.diwayou.logq;  
  2.   
  3. import com.diwayou.logq.util.MD5;  
  4. import org.jfree.chart.ChartFactory;  
  5. import org.jfree.chart.ChartFrame;  
  6. import org.jfree.chart.JFreeChart;  
  7. import org.jfree.chart.plot.PlotOrientation;  
  8. import org.jfree.data.xy.DefaultXYDataset;  
  9. import org.jfree.data.xy.XYDataset;  
  10.   
  11. import java.io.UnsupportedEncodingException;  
  12. import java.util.concurrent.ExecutorService;  
  13. import java.util.concurrent.Executors;  
  14.   
  15. /** 
  16.  * Email: [email protected] 
  17.  * User: diwayou 
  18.  * Date: 13-3-26 
  19.  * Time: 下午5:48 
  20.  */  
  21. public class LogQ {  
  22.     public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {  
  23.         String s = "alibabagogogo";  
  24.         final byte[] message = s.getBytes("GBK");  
  25.         int coreNum = Runtime.getRuntime().availableProcessors();  
  26.         int scale = 100;  
  27.         System.out.println(String.format("Core size is %d", coreNum));  
  28.   
  29.         int TEST_TIMES = 1;  
  30.         long startTime, endTime;  
  31.         DefaultXYDataset xyDataset = new DefaultXYDataset();  
  32.         double[][] elapse = new double[2][scale];  
  33.         for (int j = 0; j < scale; j++) {  
  34.             ExecutorService executorService = Executors.newFixedThreadPool(scale);  
  35.             startTime = System.nanoTime();  
  36.             for (int i = 0; i < TEST_TIMES; i++) {  
  37.                 executorService.submit(new Runnable() {  
  38.   
  39.                     @Override  
  40.                     public void run() {  
  41.                         MD5.Digest(message);  
  42.                     }  
  43.                 });  
  44.             }  
  45.             executorService.shutdown();  
  46.             endTime = System.nanoTime();  
  47.             System.out.println(String.format("Pool size %d, elapse time %d", j, (endTime - startTime)));  
  48.   
  49.             elapse[0][j] = j;  
  50.             if (j == 0) {  
  51.                 elapse[1][j] = 0;  
  52.             } else {  
  53.                 elapse[1][j] = endTime - startTime;  
  54.             }  
  55.   
  56.         }  
  57.         xyDataset.addSeries("Synchronized", elapse);  
  58.   
  59.   
  60.         elapse = new double[2][scale];  
  61.         for (int j = 0; j < scale; j++) {  
  62.             ExecutorService executorService = Executors.newFixedThreadPool(scale);  
  63.             startTime = System.nanoTime();  
  64.             for (int i = 0; i < TEST_TIMES; i++) {  
  65.                 executorService.submit(new Runnable() {  
  66.   
  67.                     @Override  
  68.                     public void run() {  
  69.                         MD5 md5 = new MD5();  
  70.                         md5.digest(message);  
  71.                     }  
  72.                 });  
  73.             }  
  74.             executorService.shutdown();  
  75.             endTime = System.nanoTime();  
  76.             System.out.println(String.format("Pool size %d, elapse time %d", j, (endTime - startTime)));  
  77.             elapse[0][j] = j;  
  78.             if (j == 0) {  
  79.                 elapse[1][j] = 0;  
  80.             } else {  
  81.                 elapse[1][j] = endTime - startTime;  
  82.             }  
  83.         }  
  84.         xyDataset.addSeries("NewEveryTime", elapse);  
  85.         displayChart("Synchronized VS NewEveryTime", xyDataset);  
  86.     }  
  87.   
  88.     private static void displayChart(String title, XYDataset dataset) {  
  89.         JFreeChart xyLineChart = ChartFactory.createXYLineChart(title, "Pool Size""Elapse Time", dataset,  
  90.                 PlotOrientation.VERTICAL, truetruetrue);  
  91.         ChartFrame chartFrame = new ChartFrame("Stat Result", xyLineChart);  
  92.         chartFrame.pack();  
  93.         chartFrame.setVisible(true);  
  94.     }  
  95. }  

 运行结果如下:

(1)第一次
java MD5 多线程环境下共享锁还是每次创建一个新的MessageDigest_第1张图片
 
Core size is 8
Pool size 0, elapse time 2012874
Pool size 1, elapse time 839776
..............
(2)第二次
java MD5 多线程环境下共享锁还是每次创建一个新的MessageDigest_第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一个。
本人菜鸟一个,分析有误还请大家支出,随便批评,这样我才能进步。

转载于:https://my.oschina.net/diwayou/blog/125570

你可能感兴趣的:(java MD5 多线程环境下共享锁还是每次创建一个新的MessageDigest)