共享或不共享java加密服务提供程序 解决方案 MessageDigest=>根据需要经常创建新实例 KeyFactory=>使用单个共享实例 SecureRandom=>使用 密码=>使用 问题:

共享或不共享java加密服务提供程序 解决方案 MessageDigest=>根据需要经常创建新实例 KeyFactory=>使用单个共享实例 SecureRandom=>使用 密码=>使用 问题:,java,pool,Java,Pool,在编写安全性框架时,我面临着一个经常性的难题:“要共用还是不共用” 这个问题基本上分为两个“组”: 第1组:SecureRandom,因为对nextBytes(…)的调用是同步的,它可能成为WebApp/多线程应用程序的瓶颈 第2组:加密服务提供商,如消息摘要,签名,密码,密钥工厂。。。(因为getInstance()?)的成本) 你的意见是什么 在这些问题上你有什么习惯 2013年7月9日编辑 最后我自己花时间测试了@QwerkyShareclass,结果非常。。。令人惊讶 这门课没有我最关心

在编写安全性框架时,我面临着一个经常性的难题:“要共用还是不共用”

这个问题基本上分为两个“组”:

  • 第1组:
    SecureRandom
    ,因为对
    nextBytes(…)
    的调用是同步的,它可能成为WebApp/多线程应用程序的瓶颈

  • 第2组:加密服务提供商,如
    消息摘要
    签名
    密码
    密钥工厂
    。。。(因为
    getInstance()
    ?)的成本)

  • 你的意见是什么

    在这些问题上你有什么习惯

    2013年7月9日编辑 最后我自己花时间测试了@Qwerky
    Share
    class,结果非常。。。令人惊讶

    这门课没有我最关心的:像游泳池或游泳池

    因此,我重新编写了该类,以测试所有4个备选方案:

    • 具有同步功能的单个共享实例
    • 每个循环中都有新实例(我对可以将摘要创建拉到循环之外的情况不感兴趣)
    • GenericObject池:
    • StackObjectPool:
    我不得不将循环次数减少到100000次,因为1M在池中花费了太多时间

    我还在每个循环的末尾添加了一个
    Thread.yield()
    ,以使负载具有更好的形状

    结果(累积运行时):

    • 消息摘要
      • 新实例:420 s
      • 单例:550秒
      • StackObjectPool:800秒
      • 通用对象池:1900秒
    • 钥匙厂
      • 新实例:400s
      • 单例:350秒
      • StackObjectPool:2900秒
      • 通用对象池:3500秒
    • 安全随机
      • StackObjectPool:1600秒
      • 新实例:2300 s
      • GenericObject池:2300s
      • 单实例:2800秒
    • 密码
      • StackObjectPool:2800秒
      • 通用对象池:3500秒
      • 单实例:5100 s
      • 新实例:8000 s
    结论
    对于MessageDigest和KeyFactory来说,池是性能杀手,甚至比具有同步瓶颈的单个实例更糟糕,但是当涉及到SecureRandom和Cipher时,它们确实很有用。此测试似乎有利于缓存

    long t0 = System.currentTimeMillis();
    byte[] bytes = new byte[100];
    MessageDigest md = MessageDigest.getInstance("MD5");
    for(int i = 0; i < 1000000; i++) {
        //MessageDigest md = MessageDigest.getInstance("MD5");
        md.reset();
        md.update(bytes);
        md.digest();
    }
    System.out.println(System.currentTimeMillis() - t0);
    
    long t0=System.currentTimeMillis();
    字节[]字节=新字节[100];
    MessageDigest md=MessageDigest.getInstance(“MD5”);
    对于(int i=0;i<1000000;i++){
    //MessageDigest md=MessageDigest.getInstance(“MD5”);
    md.reset();
    md.update(字节);
    医学文摘();
    }
    System.out.println(System.currentTimeMillis()-t0);
    

    当md在循环外时,打印579,在循环内时打印-953

    如果您给100个线程访问共享的
    MessageDigest
    并让它们分别计算1000000个哈希,那么在我的机器上,第一个线程在70160ms内完成,最后一个线程在98748ms内完成

    如果每次线程都创建一个新的
    MessageDigest
    实例,那么第一个线程将在43392ms内完成,最后一个线程将在58691ms内完成

    编辑:
    事实上,在这个示例中,只有两个线程,创建新实例的示例运行得更快

    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();
      }
    
    }
    
    import java.security.MessageDigest;
    导入java.security.NoSuchAlgorithmException;
    导入java.util.concurrent.ExecutorService;
    导入java.util.concurrent.Executors;
    公开股{
    最终字节[]字节=新字节[100];
    最终消息摘要sharedDigest;
    最终服务池;
    int线程=100;
    Share()抛出NoSuchAlgorithmException{
    sharedDigest=MessageDigest.getInstance(“MD5”);
    pool=Executors.newFixedThreadPool(线程);
    }
    void go(){
    
    对于(int i=0;iReuse归结为昂贵的创建开销和同步访问开销之间的折衷。只有您可以选择,这取决于您的使用情况。请注意,这两个组之间没有区别,如果您在线程之间共享实例,您必须同步访问
    MessageDigest
    等。当然@Qwerky有任何选择吗n需要线程/使用块之间的同步。问题是你如何在应用程序中处理这些类?这太好了——我希望这样的信息可以更容易地用于各种类。真的,我希望它在标准Javadoc中。所显示的是
    MessageDigest
    有一个创建开销。在单线程情况下,重用总是更快。