上述代码中有两个方法,分别是encrypt和decryption,分别表示加密和解密。
  为了简单,可以被多个线程共用,因此将CountDownLatch定义为static。
  在main函数中,创建了固定大小的线程池(200个线程,这个可以根据需要进行调整)和用于加密的payload和秘钥secureKey。Start用于记录开始时间,通过后的System.currentTimeMillis()-start即可获得程序的运行时间。在for循环中,并发执行了count(10W)次payload的加密和解密,每个线程在执行完一个任务后会调用latch.countDown(),而主程序在循环后使用latch.await(),等待所有线程执行结束后,统计执行时间,输出消耗时间,后关闭线程池。
  一般在做对比或者寻找瓶颈时,才会使用性能测试,下面给出一个性能对比的例子。
  在前面的代码中,无论是encrypt,还是decrypt,都有如下内容:
  Cipher cipher = CliperInstance.getInstance();//创建密码器
  //Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
  现在我们要对比每次加密和解密都执行Cipher.getInstance方法和对每一个线程自己都维护一个Cipher实例的性能差别。(我们都知道,Ciper.getInstance的调用时很耗时的)。
  使用Ciphercipher = CliperInstance.getInstance(); 时,执行100W次加密和解密,大约使用11.2s,而使用Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); 大约需要39.6s,因此在进行加密、解密、签名等算法时,好每个线程维护一个加密、解密或者签名的实例(这些实例是不能再多线程间共享的,如上例所示,调用init和doFinal必须在一个线程内完成,如果全局共享同一个实例,A调用init,B也调用了init,当A调用doFinal时,此时实例内的数据已经不是A的了,会出现异常)。
  顺便说一下,如果要求每个线程有自己的实例的情况下(如上面的加密和解密等),那么可以使用ThreadLocal,在使用ThreadLocal时,重写内部的initialValue 方法,每次调用ThreadLocal的get方法时,ThreadLocal实例先到自己的Map中寻找有没有当前线程对应的Instance,如果存在,将Instance返回,如果不存在,调用initialValue去创建一个Instance,并将新Instance放到Map中后,返回。详情参看JDK文档。
  需要注意的是,CliperInstance 没必要非点单写成一个类,这里是为了让代码更容易懂。