generador de números pseudoaleatorios java

Conceptos básicos sobre números aleatorios

1. Clasifica las propiedades de los números aleatorios:

  • Aleatoriedad : un número pseudoaleatorio débil que cumple esta propiedad se denomina número pseudoaleatorio débil. Este tipo de número aleatorio solo se puede usar en aplicaciones generales y no en criptografía, como la clase java.util.Random en Java.
  • Imprevisibilidad : un número pseudoaleatorio fuerte que cumple esta propiedad. En criptografía, los números aleatorios requieren imprevisibilidad además de aleatoriedad, es decir, es imposible adivinar el siguiente número aleatorio a través de datos históricos.
  • No repetibilidad : un número aleatorio verdadero que cumple esta propiedad. Los números aleatorios irrepetibles no se pueden generar solo con software, porque el software de computadora solo tiene un estado interno limitado, siempre que el estado interno sea el mismo, se debe generar el mismo número, lo que también se llama ciclo. Para satisfacer la no repetibilidad, solo se puede obtener a través de fenómenos físicos, como temperatura, sonido, etc., que requieren soporte de hardware. Por ejemplo, la CPU Intel proporciona un generador de números aleatorios.

Las propiedades anteriores se vuelven más estrictas a medida que disminuyen y tienen propiedades inclusivas: por ejemplo, la no repetibilidad debe tener aleatoriedad e imprevisibilidad; la imprevisibilidad debe tener aleatoriedad.

Explicación: La secuencia numérica generada al lanzar dados repetidamente no es repetible.

2. Resumen:

Los números aleatorios pueden generarse mediante hardware o software. Los números aleatorios generados por hardware, como el calor y el sonido recopilados por sensores, son fenómenos naturales impredecibles y no repetitivos, llamados números aleatorios verdaderos, también llamados Generador de números aleatorios (RNG).

Los números aleatorios generados por software se denominan números aleatorios (Pseudo Random Number Generator PRNG). Debido a que el estado del software es limitado, debe ser cíclico. Los números aleatorios se dividen en: aleatorio débil y aleatorio fuerte.

A continuación, presentamos el uso de números pseudoaleatorios en Java.

Introducción al azar

Antes de JDK7, java.util.Random era una clase de herramienta de generación de números aleatorios ampliamente utilizada. Además, la generación de números aleatorios en java.lang.Math también era una instancia de java.util.Random. La clase Random utiliza un algoritmo de congruencia lineal para generar números aleatorios . Este algoritmo es un algoritmo de números pseudoaleatorios débil y no tiene imprevisibilidad, por lo que no se puede utilizar en criptografía.

1. La clase Random es segura para subprocesos:

La generación de números aleatorios está relacionada con la semilla y, para garantizar la seguridad de los subprocesos, Random utiliza el mecanismo CAS para garantizar la seguridad de los subprocesos. Desde el método next(), podemos ver que la semilla se modifica mediante CAS continuo. Si se encuentra en un escenario de alta concurrencia, puede provocar que CAS falle, lo que provocará giros continuos y puede provocar que la CPU sea demasiado alta.

Además, CAS también se utiliza al crear Random:

También se puede ver aquí que la semilla aleatoria predeterminada es adecuada para el momento actual. Por supuesto, la semilla también se puede especificar en el constructor.

 2. uso

Random random = new Random();
random.nextInt(); //返回[0, max)的随机正整数
random.nextInt(10); //返回[0,10)的随机正整数
        
double rd1 = Math.random(); //返回[0.0,1.0)的随机小数

Introducción a ThreadLocalRandom

ThreadLocalRandom hereda de Random, que también es un número pseudoaleatorio débil. Resuelve principalmente el problema de rendimiento de generación de números aleatorios de Random en un entorno de alta concurrencia.

1. ¿Por qué ThreadLocalRandom es seguro para subprocesos?

Similar al principio de ThreadLocal, guarda la semilla aleatoria en la variable threadLocalRandomSeed del objeto Thread actual, de modo que cada hilo tiene su propia semilla aleatoria y logra el aislamiento a nivel de hilo, por lo que ThreadLocalRandom no necesita pasar bloqueos de giro y cas a Garantiza la seguridad del hilo de la semilla aleatoria. En escenarios de alta concurrencia, la eficiencia será relativamente alta.

2. Implementación del código fuente:

ThreadLocalRandom utiliza ampliamente el método inseguro para manipular variables directamente a través de direcciones. Para conocer el uso de unfafe, consulte: Clase mágica de Java 3.0: análisis de aplicaciones inseguras.nota

1) La semilla se define en la clase Thread y la SEED definida en ThreadLocalRandom representa el desplazamiento de memoria del atributo threadLocalRandomSeed de la clase Thread

ThreadLocalRandom.java
private static final sun.misc.Unsafe UNSAFE;
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
static {
    try {
        UNSAFE = sun.misc.Unsafe.getUnsafe();
        Class<?> tk = Thread.class;
        SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));
        PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));
    } catch (Exception e) {
        throw new Error(e);
    }
}

Thread.java
@sun.misc.Contended("tlr")
//当前Thread的随机种子 默认值是0
long threadLocalRandomSeed;
@sun.misc.Contended("tlr")
//用来标志当前Thread的threadLocalRandomSeed是否进行了初始化 0代表没有,非0代表已经初始化 默认值是0
int threadLocalRandomProbe;

Nota: @sun.misc.Contende es para evitar el intercambio falso.

2) Cuando un hilo llama al método actual de ThreadLocalRandom, ThreadLocalRandom es responsable de inicializar la variable threadLocalRandomSeed del hilo que llama, es decir, inicializar la semilla;

public static ThreadLocalRandom current() {
        // 获取当前线程的
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
}

static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
}

Descripción: utilice el método putLong de inseguro para manipular directamente la memoria de acuerdo con la dirección de compensación del atributo del objeto.

3) Cuando se llama al método nextInt de ThreadLocalRandom, la variable threadLocalRandomSeed del hilo actual en realidad se obtiene como la semilla actual para calcular el nuevo valor, y luego la nueva semilla se actualiza a la variable threadLocalRandomSeed del hilo actual. (Algoritmo de congruencia lineal: calcule el número aleatorio de acuerdo con la semilla y luego actualice el número aleatorio a una nueva semilla para el siguiente cálculo):

public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        int r = mix32(nextSeed());
        int m = bound - 1;
        if ((bound & m) == 0) // power of two
            r &= m;
        else { // reject over-represented candidates
            for (int u = r >>> 1;
                 u + m - (r = u % bound) < 0;
                 u = mix32(nextSeed()) >>> 1)
                ;
        }
        return r;
}
final long nextSeed() {
        Thread t; long r; // read and update per-thread seed
        UNSAFE.putLong(t = Thread.currentThread(), SEED,
                       r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
}

En el caso de alta concurrencia, intente utilizar ThreadLocalRandom para generar números aleatorios- Tencent Cloud Developer Community-腾讯云

3. Descripción:

En la arquitectura ssh, ni Random ni ThreadLocalRandom se pueden definir en variables globales, pero deben definirse e inicializarse en métodos. De lo contrario, es posible que diferentes hilos utilicen la misma semilla, por lo que la secuencia de números aleatorios creada puede ser la misma. Ahora mismo:

Introducción a java.Security.SecureRandom

También hereda java.util.Random, que proporciona un potente algoritmo de números pseudoaleatorios que se puede utilizar en criptografía.

1. ¿Cómo se generan los números aleatorios en Linux PRNG (generador de números pseudoaleatorios)?

El kernel de Linux utiliza la entropía para describir la aleatoriedad de los datos. La entropía es una cantidad física que describe el grado de caos y desorden de un sistema. Cuanto mayor es la entropía, peor es el orden del sistema. El kernel mantiene un grupo de entropía que recopila el ruido ambiental de los controladores de dispositivos y otras fuentes. El PRNG en el kernel es un dispositivo de caracteres aleatorio, que proporciona 2 dispositivos de caracteres para uso de procesos en modo usuario: /dev/random y /dev/urandom:

  • /dev/random es adecuado para solicitudes que requieren una calidad de números aleatorios relativamente alta. Cuando los datos en el grupo de entropía son insuficientes, al leer el dispositivo dev/random, devolverá bytes aleatorios que son menores que el número total de ruidos en el piscina de entropía. /dev/random genera claves públicas altamente aleatorias o pads de un solo uso. Si el grupo de entropía está vacío, las lecturas en /dev/random se bloquearán hasta que se haya recopilado suficiente ruido ambiental. Este diseño convierte a /dev/random en un verdadero generador de números aleatorios, que proporciona la mayor entropía posible de datos aleatorios.
  • /dev/urandom, un generador de números aleatorios sin bloqueo que reutiliza datos en el grupo de entropía para generar datos pseudoaleatorios. Esto significa que las lecturas de /dev/urandom no se bloquearán, pero su salida puede tener menos entropía que /dev/random. Se puede utilizar como generador de números pseudoaleatorios para generar cifrados de menor potencia donde la aleatoriedad es aceptable para la mayoría de las aplicaciones.

2. Obtenga el método de instancia SecureRandom:

Hay dos formas de obtener una instancia de SecureRandom:

  • SecureRandom random = new SecureRandom(); //También puede especificar la semilla a través de parámetros, pero esto no se recomienda.
  • Aleatorio aleatorio = SecureRandom.getInstanceStrong();

La diferencia entre estos dos métodos: en Linux, el primero usa /dev/urandom como generador de números pseudoaleatorios; el segundo usa /dev/random como generador de números pseudoaleatorios. Por lo tanto, este último puede bloquearse al llamar a métodos como nextInt para obtener números aleatorios debido a datos insuficientes en el grupo de entropía del sistema.

1) Experimento:

import java.security.SecureRandom;
import java.util.Random;
import java.security.NoSuchAlgorithmException;
public class RandomTest {
    public static void main(String... str) throws NoSuchAlgorithmException {
        //SecureRandom random = new SecureRandom();
        Random random = SecureRandom.getInstanceStrong();
        int out = 0;
        for (int i = 0; i < 1<<20 ; i++) {
            out ^= random.nextInt();
        }
        System.out.println(out);
    }
}

El programa crea instancias de SecureRandom de dos maneras diferentes y luego genera números aleatorios 1.048.576 veces. Después de realizar pruebas en Linux, se descubre que si la instancia de SecureRandom se crea usando la sexta línea, el programa se ejecutará en 2 segundos; si se usa la séptima línea, el programa se bloqueará. Esto también verifica la conclusión anterior.

2) Seguimiento de llamadas al sistema vía strace:

El comando strace puede rastrear llamadas al sistema Linux. Entonces ejecutamos el programa anterior de la siguiente manera:

A. Utilice la línea 6 para crear una instancia de SecureRandom, luego compile javac y ejecútelo de la siguiente manera:

javac RandomTest.java
strace -ff -o ./c.strace  java RandomTest
1839562559
#会在当前目录产生大量的strace文件,通过执行如下命令搜索"dev/random"所在的文件
grep "dev" -r -n ./
./c.strace.22057:6:openat(AT_FDCWD, "/sys/devices/system/cpu", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
./c.strace.22057:966:access("/dev/random", R_OK)             = 0
./c.strace.22057:967:access("/dev/random", R_OK)             = 0
./c.strace.22057:968:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:969:open("/dev/random", O_RDONLY)           = 5
./c.strace.22057:970:fstat(5, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:971:open("/dev/urandom", O_RDONLY)          = 6
./c.strace.22057:972:fstat(6, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.22057:973:access("/dev/random", R_OK)             = 0
./c.strace.22057:974:access("/dev/random", R_OK)             = 0
./c.strace.22057:975:open("/dev/random", O_RDONLY)           = 7
./c.strace.22057:976:fstat(7, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:977:open("/dev/random", O_RDONLY)           = 8
./c.strace.22057:978:fstat(8, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0
./c.strace.22057:979:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:980:access("/dev/urandom", R_OK)            = 0
./c.strace.22057:981:open("/dev/urandom", O_RDONLY)          = 9
./c.strace.22057:982:fstat(9, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.22057:985:open("/dev/urandom", O_RDONLY)          = 10
./c.strace.22057:986:fstat(10, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 9), ...}) = 0
./c.strace.21889:96:fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
./c.strace.22065:10:open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 3

abrir un archivo

Se puede ver que aunque la capa inferior de SecureRandom.java abrirá los dos archivos /dev/random y /dev/urandom, la lectura final es la número 6, que es el archivo /dev/urandom.

B. Utilice la línea 7 para crear una instancia de SecureRandom, luego compílela con javac y ejecútela de la misma manera.

strace -ff -o c.strace  java RandomTest
#会一直在这里阻塞住,没有执行结果。。。

Busque el archivo de la misma manera y ábralo:

Se puede ver que la capa inferior de SecureRandom.java abrirá los dos archivos /dev/random y /dev/urandom, pero la lectura final es la número 8, que es el archivo /dev/random y la llamada al sistema de lectura. ha sido bloqueado..

Linux: ¿Java SecureRandom no se bloquea? ¿Cómo? - Intercambio de pilas de seguridad de la información

Utilice SecureRandom para generar registros de minería de números aleatorios - Comunidad de desarrolladores de Tencent Cloud - Tencent Cloud

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/liuxiao723846/article/details/128698552
Recomendado
Clasificación