java uuid和SecureRandom性能详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java uuid和SecureRandom性能详解相关的知识,希望对你有一定的参考价值。



 

1. java.security.SecureRandom源码分析

 

jdk产生uuid的代码:

 

   public static UUID randomUUID() {

        SecureRandom ng =Holder.numberGenerator;

 

        byte[] randomBytes = newbyte[16];

        ng.nextBytes(randomBytes);

        randomBytes[6] &= 0x0f;  /* clear version       */

        randomBytes[6]  |=0x40;  /* set to version 4     */

        randomBytes[8] &= 0x3f;  /* clear variant       */

        randomBytes[8]  |=0x80;  /* set to IETF variant  */

        return newUUID(randomBytes);

    }

 使用了SecureRandom.next*的方法。

 

在使用SecureRandom产生下一个随机数的时候调用nextLong或者nextBytes,最终会调用SecureRandomnextBytes。而nextBytes是一个同步方法,会产生性能瓶颈。

    public long nextLong() {

        // it‘s okay that the bottom wordremains signed.

        return ((long)(next(32)) << 32)+ next(32);

    }

 

    final protected int next(int numBits) {

        int numBytes = (numBits+7)/8;

        byte b[] = new byte[numBytes];

        int next = 0;

 

        nextBytes(b);

        for (int i = 0; i < numBytes; i++)

            next = (next << 8)+ (b[i] & 0xFF);

        return next >>> (numBytes*8 -numBits);

    }

nextBytes是一个同步的方法

synchronized public void nextBytes(byte[] bytes) {

       secureRandomSpi.engineNextBytes(bytes);

    }

secureRandomSpi被初始化为sun.security.provider.SecureRandom

secureRandomSpiSecureRandom.NativePRNG的一个实例。从securityprovider列表中可以看到,SecureRandom.NativePRNGsun.security.provider.NativePRNG提供服务。

Provider: Set SUN provider property[SecureRandom.NativePRNG/sun.security.provider.NativePRNG]

分析openjdk的源码可以看到,NativePRNG.engineNextBytes调用了NativePRNG.RandomIO.ensureBufferValid,而ensureBufferValid直接从urandom读取数据:

private void ensureBufferValid() throws IOException {
            ...
            readFully(urandomIn, urandomBuffer);
            ...
        }

通过测试可以发现,hotspot需要使用配置项"-Djava.security.egd=file:/dev/./urandom"才能从urandom读取数据,这里openjdk做了优化,直接从urandom读取数据。

 

 

 

2. JUG源码分析

 

JUGcom.fasterxml.uuid.impl.TimeBasedGenerator.generate()可以产生基于时间的UUID。由于在同步块中,仅做了整数的运算,因此,同步影响较小。

原理是在初始化的时候,根据网卡、随机数、计算出了一部分固定的数据:

public TimeBasedGenerator(EthernetAddress ethAddr , UUIDTimertimer)

  {

  byte [] uuidBytes = new byte [16];

  if (ethAddr == null) {

  ethAddr = EthernetAddress.constructMulticastAddress();

  }

  // initialize baseline with MAC address info

  _ethernetAddress = ethAddr ;

  _ethernetAddress .toByteArray(uuidBytes , 10);

  // and add clock sequence

  int clockSeq = timer.getClockSequence();

  uuidBytes [UUIDUtil. BYTE_OFFSET_CLOCK_SEQUENCE] = ( byte )(clockSeq >> 8);

  uuidBytes [UUIDUtil. BYTE_OFFSET_CLOCK_SEQUENCE+1] = ( byte) clockSeq ;

  long l2 = UUIDUtil.gatherLong (uuidBytes , 8);

  _uuidL2 = UUIDUtil.initUUIDSecondLong( l2);

  _timer = timer ;

  }

 

在调用generate的时候,根据当前时间,加上一个counter,生成另一半随机数:

public UUID generate()

  {

  final long rawTimestamp = _timer.getTimestamp ();

  // Time field components are kind of shuffled, need toslice:

  int clockHi = ( int) ( rawTimestamp >>> 32);

  int clockLo = ( int) rawTimestamp ;

  // and dice

  int midhi = ( clockHi << 16) | ( clockHi >>>16);

  // need to squeeze in type (4 MSBs in byte 6, clock hi)

  midhi &= ~0xF000; // remove high nibble of 6th byte

  midhi |= 0x1000; // type 1

  long midhiL = ( long) midhi ;

  midhiL = ((midhiL << 32) >>> 32); // to getrid of sign extension

  // and reconstruct

  long l1 = ((( long) clockLo ) << 32) | midhiL ;

  // last detail: must force 2 MSB to be ‘10‘

  return new UUID( l1, _uuidL2);

  }

 

 

public final synchronized long getTimestamp ()

  {

  long systime = System.currentTimeMillis ();

 ......

  _clockCounter &= 0xFF;

 

  systime *= kClockMultiplierL;

  systime += kClockOffset; //由于uuid需要GregorianCalendar,这里将1970年的时间加到系统时间

 

  // Plus add the clock counter:

  systime += _clockCounter ;

  // and then increase

  ++ _clockCounter ;

  return systime ;

  }

 



本文出自 “勤似春芽” 博客,请务必保留此出处http://leo01.blog.51cto.com/694903/1795447

以上是关于java uuid和SecureRandom性能详解的主要内容,如果未能解决你的问题,请参考以下文章

UUID/签名/加解密工具并发场景下隐藏的性能瓶颈问题

UUID/签名/加解密工具并发场景下隐藏的性能瓶颈问题

UUID/签名/加解密工具并发场景下隐藏的性能瓶颈问题

UUID/签名/加解密工具并发场景下隐藏的性能瓶颈问题

为什么分布式数据库中不使用uuid作为主键?

java web项目部署在linux系统出现第一次登录高延迟的问题