Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。该算法的文件号为RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)。
查看MessageDigest源码
public void update(byte[] input) { engineUpdate(input, 0, input.length); state = IN_PROGRESS; }
可以看到这里调用了engineUpdate方法,此方法进行一个更新操作。
然后state属性的状态就被改变了,表明当前计算正在处理过程中。
state默认属性
private int state = INITIAL;
然后需要调用MessageDigest.digest()方法计算哈希值
public byte[] digest() { /* Resetting is the responsibility of implementors. */ byte[] result = engineDigest(); state = INITIAL; return result; }
到这里已经完成了MD5值的计算,state属性恢复初始状态,如果想要重用MessageDigest对象,还需要调用MessageDigest.reset()方法进行重置,以免这次计算数据会对下一次的计算造成影响,从而导致计算结果错误。
而我所遇到的问题就是,在MessageDigest在多线程的环境下,Thread-1的计算还没有完成的情况下,Thread-2又开始使用该MessageDigest对象进行下一次的计算,Thread-2修改了MessageDigest的状态,Thread-1使用被修改过后的MessageDigest进行计算,从而导致了计算结果错误。
解决方案有两个:
1、加锁来共享同一个MessageDigest;
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 }; public MD5() { try { md5_ = MessageDigest.getInstance("MD5");} // MD5 is supported catch ( NoSuchAlgorithmException e ) {}; // safe to swallow } /** * */ 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] & 0xF0 ) >> 4 ] ; hex_bytes[j+1] = ToHex_[ b[i] & 0xF ]; j+=2; } return new String( hex_bytes ); } }
2、每次新创建一个MessageDigest;
class MD5_test { public final static String MD5(String s) { char hexDigits[] = { ‘0‘ , ‘1‘ , ‘2‘ , ‘3‘ , ‘4‘ , ‘5‘ , ‘6‘ , ‘7‘ , ‘8‘ , ‘9‘ , ‘a‘ , ‘b‘ , ‘c‘ , ‘d‘ , ‘e‘ , ‘f‘ }; try { byte [] strTemp = s.getBytes(); MessageDigest mdTemp = MessageDigest.getInstance("MD5" ); mdTemp.update(strTemp); byte [] md = mdTemp.digest(); int j = md.length; char str[] = new char [j * 2 ]; int k = 0 ; for ( int i = 0 ; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf ]; str[k++] = hexDigits[byte0 & 0xf ]; } return new String(str); } catch (Exception e) { return null ; } } public static void main(String[] args) { // MD5_Test aa = new MD5_Test(); System.out.print(MD5_test.MD5("a" )); // System.out.print(MD5_test.MD5("%7B%7DAHRCU" )); } }
这两种方案都可解决并发问题。