解决方案

  • MessageDigest =>根据需要创建新实例
  • KeyFactory =>使用单个共享实例
  • SecureRandom =>使用StackObjectPool
  • 密码=>使用StackObjectPool

  • 问题

    在对安全性框架进行编码时,我经常遇到两难的问题:“要合并还是不合并”

    基本上,这个问题分为两个“组”:
  • 组1:SecureRandom,因为对nextBytes(...)的调用已同步,并且可能成为WebApp/多线程应用程序
  • 的瓶颈
  • 第2组:加密服务提供商,例如MessageDigestSignatureCipherKeyFactory,...(由于getInstance()的成本?)

  • 你有什么意见 ?

    您对这些问题有什么习惯?

    编辑09/07/2013

    我终于花了一些时间独自测试@Qwerky Share类,我发现结果相当……令人惊讶。

    上课时我最担心的是:GenericObjectPoolStackObjectPool之类的池。

    因此,我对类(class)进行了重新设计,以测试所有4种选择:
  • 具有同步的单个共享实例gist
  • 每个循环内有新实例(当您可以将摘要创建拉到循环外时,我不感兴趣)gist
  • GenericObjectPool:gist
  • StackObjectPool:gist

  • 由于1M在池中花费了太多时间,因此我不得不将循环数降低到100000。

    我还在每个循环的末尾添加了Thread.yield(),以使负载更美观。

    结果(累计运行时间):
  • MessageDigest
  • 新实例:420 s
  • 单一实例:550 s
  • StackObjectPool:800 s
  • GenericObjectPool:1900年代
  • KeyFactory
  • 新实例:400s
  • 单一实例:350 s
  • StackObjectPool:2900 s
  • GenericObjectPool:3500 s
  • SecureRandom
  • StackObjectPool:1600 s
  • 新实例:2300 s
  • GenericObjectPool:2300秒
  • 单一实例:2800 s
  • 密码
  • StackObjectPool:2800 s
  • GenericObjectPool:3500 s
  • 单一实例:5100秒
  • 新实例:8000 s

  • 结论

    对于MessageDigest和KeyFactory,池是性能的杀手,甚至比具有同步瓶颈的单个实例还要差,而对于SecureRandom和Cipher来说,它们确实非常有用

    最佳答案

    如果让100个线程访问共享的MessageDigest,并让它们分别计算1,000,000个哈希,则在我的计算机上,第一个线程在70,160ms内完成,最后一个线程在98,748ms内完成。

    如果线程每次都创建一个新的MessageDigest实例,那么第一个线程将在43,392ms内结束,最后58,691ms结束。

    编辑:
    实际上,在此示例中,只有两个线程,该示例创建新实例的运行速度更快。

    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Share {
    
      final byte[] bytes = new byte[100];
      final MessageDigest sharedDigest;
      final ExecutorService pool;
      int threads = 100;
    
      Share() throws NoSuchAlgorithmException {
        sharedDigest = MessageDigest.getInstance("MD5");
        pool = Executors.newFixedThreadPool(threads);
      }
    
      void go() {
    
        for (int i=0; i<threads; i++) {
          pool.execute(new Runnable() {
            public void run() {
              long start = System.currentTimeMillis();
              for (int i=0; i<1000000; i++) {
                /*
                synchronized (sharedDigest) {
                  sharedDigest.reset();
                  sharedDigest.update(bytes);
                  sharedDigest.digest();
                }*/
                try {
                  MessageDigest digest = MessageDigest.getInstance("MD5");
                  digest.reset();
                  digest.update(bytes);
                  digest.digest();
                } catch (Exception ex) {
                  ex.printStackTrace();
                }
              }
              long end = System.currentTimeMillis();
              System.out.println(end-start);
              pool.shutdown();
            }
          });
        }
    
      }
    
      public static void main(String[] args) throws Exception {
        Share share = new Share();
        share.go();
      }
    
    }
    

    10-07 19:40