Больше отличных статей.
«Микросервисы — это не все, а лишь подмножество определенного домена».
С таким количеством компонентов мониторинга всегда найдется подходящий для вас
«С Нетти, что мы разрабатываем? 》
«Вероятно, это наиболее подходящая спецификация Redis».
«Портрет программиста, десять лет взлетов и падений»
Самая полезная серия:
«Наиболее часто используемый набор навыков «vim» в производственной среде Linux.
«Наиболее часто используемый набор навыков «Sed» в производственной среде Linux.
«Наиболее часто используемый набор навыков «AWK» в производственной среде Linux.
Начнем с jdk8. В основном четыре генератора случайных чисел. Какие? Есть четыре?
Далее кратко расскажем о сценариях использования этих классов, чтобы понять нюансы и благие намерения дизайнеров API.
java.util.Random
java.util.concurrent.ThreadLocalRandom
java.security.SecureRandom
java.util.SplittableRandom
Random
Чаще всего используется случайный.
используется для создания伪随机数
, который используется по умолчанию48
немного семян,线性同余公式
модифицировать. Мы можем передать в конструкторе初始seed
, или сбросить (синхронизировать) через setSeed. Семя по умолчанию — это количество наносекунд в системном времени, которое действительно велико!
Если два (более) разных экземпляра Random, используя одно и то же начальное число, вызовут один и тот же метод в одном и том же порядке, то они получат одинаковую последовательность чисел. Это не выглядит слишком случайным.Эта стратегия проектирования имеет как преимущества, так и недостатки. Преимущество заключается в том, что последовательности, генерируемые «одним и тем же начальным числом», являются согласованными, что делает процесс прослеживаемым и проверяемым (независимым от платформы и среды выполнения); его «предсказуемого» риска.
Экземпляры Random потокобезопасны.Однако одновременное использование одного и того же экземпляра java.util.Random в разных потоках может привести к конфликтам, что приведет к снижению производительности (в методе nextX CAS используется при назначении начального значения для начального числа, и результаты тестирования показывают, что потеря производительности на самом деле очень маленький). Рассмотрите возможность использования ThreadLocalRandom в многопоточном проекте. В то же время в параллельной среде нет необходимости преднамеренно использовать несколько экземпляров Random.
Случайные экземпляры не являются криптографически безопасными.Вместо этого рассмотрите возможность использования SecureRandom, чтобы получить криптографически безопасный генератор псевдослучайных чисел для использования приложениями, чувствительными к безопасности.
Random — это наиболее часто используемый класс генерации случайных чисел, который подходит для большинства сценариев.
Random random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));
random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));
random = new Random(100);
System.out.println(random.nextInt(10) + "," + random.nextInt(30) + "," + random.nextInt(50));
Три разных случайных экземпляра выше используют одно и то же начальное число. Вызывающий процесс тот же, и генерируемая в нем последовательность случайных чисел точно такая же. Результаты многократных запусков также полностью согласованы.Короче говоря, пока начальное начальное число одинаково, даже если экземпляры разные, результаты их многократного запуска непротиворечивы. Это явление согласуется с тем, что было сказано выше.
Если начальное значение не указано в конструкторе Random, но в качестве доминирующей переменной используются наносекунды системного времени по умолчанию, результаты выполнения трех случайных экземпляров будут разными. Результаты многократного выполнения не одинаковы. Можно видеть, что то, является ли начальное число случайным или нет, в определенной степени также определяет случайность результатов, генерируемых Random.
Таким образом, в распределенной или многопоточной среде, если экземпляр Random находится в потоке задачи с одним и тем же кодом, возможно, что эти распределенные процессы или потоки будут создавать одно и то же значение последовательности. Это также введение класса ThreadLocalRandom, когда JDK 7 представил ForkJoin.
ThreadLocalRandom
Роль этого класса — изолировать генератор случайных чисел от текущего потока. Этот класс наследуется от java.util.Random, и, подобно глобальному генератору Random, используемому классом Math, ThreadLocalRandom инициализируется с помощью внутреннего сгенерированного начального числа, которое иначе было бы не поддающимся изменению.
Использование ThreadLocalRandom в параллельных программах обычно имеет меньше накладных расходов и конфликтов. ThreadLocalRandom особенно подходит, когда несколько задач (например, каждая ForkJoinTask) используют случайные числа параллельно в пуле потоков.
Помните, что экземпляры ThreadLocalRandom не должны совместно использоваться несколькими потоками.
Инициализация ThreadLocalRandom является приватной, поэтому начальное значение нельзя установить через конструктор, а его метод setSeed также переписан и не поддерживается (выдает исключение). По умолчанию исходным доминирующим значением переменной для каждого экземпляра ThreadLocalRandom является системное время (в наносекундах):
private static long initialSeed() {
String sec = VM.getSavedProperty("java.util.secureRandomSeed");
if (Boolean.parseBoolean(sec)) {
byte[] seedBytes = java.security.SecureRandom.getSeed(8);
long s = (long)(seedBytes[0]) & 0xffL;
for (int i = 1; i < 8; ++i)
s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
return s;
}
return (mix64(System.currentTimeMillis()) ^
mix64(System.nanoTime()));
}
В соответствии с реализацией начального числа инициализации мы также можем добавить "-Djava.util.secureRandomSeed=true" через параметр запуска JVM. В это время исходной переменной больше не будет системное время, а сгенерированный случайный фактор. классом SecureRandom. В качестве начального семени ThreadLoalRandom.
Он действительно достаточно круглый.
Из исходного кода я не увидел, что Thread-ID используется в качестве переменной для генерации начальных чисел, и алгоритм генерации случайных чисел в методе nextX также непротиворечив. Это означает, что если начальное время ThreadLocalRandom для нескольких потоков точно такое же, сгенерированная случайная последовательность также будет одинаковой, когда вызывающий метод и процесс одинаковы; в определенной степени "-Djava.util.secureRandom=true" может избежать этой проблемы.
ThreadLocalRandom не использует ThreadLocal для поддержки внутреннего хранения данных и т. д., а напрямую использует UnSafe для управления адресом памяти начального атрибута в текущей ссылке на объект Thread и выполнения операций с данными.Я восхищаюсь гениальным подходом SUN.
SecureRandom
Он также наследуется от Random, который предоставляет криптографически стойкий генератор случайных чисел (RNG), который, как минимум, соответствует FIPS 140-2 «Требования безопасности для криптографических модулей». Кроме того, SecureRandom должен создавать недетерминированные выходные данные. Следовательно, любой начальный материал, передаваемый объекту SecureRandom, должен быть непредсказуемым, а все выходные последовательности SecureRandom должны быть криптографически стойкими. (Официальный текст, на самом деле я мало что о нем знаю)
SecureRandom по умолчанию поддерживает две реализации алгоритма шифрования RNG:
1) Поставщик алгоритма "SHA1PRNG" sun.security.provider.SecureRandom
2) Провайдер "NativePRNG" sun.security.provider.NativePRNG
По умолчанию это "SHA1PRNG", реализация, предоставленная SUN. Кроме того, вы можете использовать «-Djava.security=file:/dev/urandom» (рекомендуется) или «-Djava.security=file:/dev/random», чтобы указать использование локального случайного алгоритма Linux, то есть NativePRNG. ; где "/dev/random" и "/dev/urandom" реализованы по-разному на разных unix-* платформах, и производительность тоже разная. Рекомендуется использовать "/dev/urandom".
Копией /dev/random является /dev/urandom («разблокированный», неблокирующий генератор случайных чисел), который повторно использует данные из энтропийного пула для генерации псевдослучайных данных. Это означает, что чтение в /dev/urandom не будет блокироваться, но энтропия его вывода может быть меньше, чем у /dev/random. Его можно использовать в качестве генератора псевдослучайных чисел для генерации менее надежных паролей и не рекомендуется для создания высоконадежных долгосрочных паролей.
Внутренняя реализация алгоритма сложнее, я тестировал, и разница в производительности не слишком большая (среда JDK 8). SecureRandom также является потокобезопасным.
Из анализа выходных результатов,Независимо от того, указано ли начальное семя SecureRandom, результаты нескольких запусков одного экземпляра совершенно разные.; Несколько разных экземпляров SecureRandom, независимо от того, указано ли начальное число или нет, даже если указано одно и то же начальное начальное число, результаты одновременного запуска будут совершенно разными.
SecureRandom наследуется от Random, но перезаписывает базовый метод в методе nextX, но он по-прежнему основан на CAS Random, а базовый метод SecureRandom также использует синхронизацию, поэтому в параллельной среде производительность хуже, чем в Random.
SplittableRandom
Новый API JDK 8 в основном применим к межпотоковым операциям в форме Fork/join. Он не наследует класс java.util.Random.
Различные экземпляры SplittableRandom с одним и тем же начальным числом или одним и тем же SplittableRandom, результаты согласуются для нескольких запусков. Это согласуется со случайным.
Не является потокобезопасным и не может использоваться одновременно.(Об ошибке не будет сообщено, но несколько потоков могут получить одно и то же случайное число одновременно во время параллелизма)
Как и в случае с ThreadLocalRandom, параметр "-Djava.util.secureRandom=true" поддерживается, но только при использовании конструктора по умолчанию SecureRandom будет использоваться для создания начального начального числа. То есть, когда начальное начальное значение не указано, один и тот же экземпляр SplittableRandom запускается несколько раз или запускаются разные экземпляры, и результаты будут разными.
Существует метод split(), который используется для создания и возврата нового экземпляра, который имеет некоторое неизменяемое состояние. Следует отметить, что новый экземпляр SplittableRandom, сгенерированный с помощью split, не имеет внутренней конкуренции за параллелизм данных с исходным экземпляром, а также не изменяет и не продолжает последовательность генерации случайных чисел исходного экземпляра (то есть согласованность сгенерированной случайной последовательности двух экземпляров согласуется с исходным экземпляром. Экземпляр не имеет значения, он ближе только на уровне статистических значений); но в случае согласованности кода результаты последовательности случайных чисел всегда согласуются (если указывается начальное начальное число вместо значения по умолчанию) при многократном запуске эта производительность такая же, как у Random и ThreadLocalRandom.
public SplittableRandom split() {
return new SplittableRandom(nextLong(), mixGamma(nextSeed()));
}
Образец кода.
System.out.println("第一段");
SplittableRandom random = new SplittableRandom(100);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
SplittableRandom _random = random.split();
for (int i=0; i < 5; i++) {
System.out.println("---" + _random.nextInt(100));
}
}
});
thread.start();
thread.join();
for (int i=0; i < 5; i++) {
System.out.println("+++" + random.nextInt(100));
}
System.out.println("第二段");
SplittableRandom _random = new SplittableRandom(100);
for (int i=0; i < 10; i++) {
System.out.println("..." + _random.nextInt(100));
}
Результаты.
第一段
---71
---85
---10
---60
---98
+++44
+++87
+++77
+++67
+++72
第二段
...92
...30
...44
...87
...77
...67
...72
...23
...9
...64
Судя по результату выполнения, случайный экземпляр, сгенерированный с помощью split, не имеет сходства с результатом выполнения исходного экземпляра, но разные экземпляры SplittableRandom (независимо от того, был ли выполнен split) создают последовательность случайных чисел, согласованную.
Тестирование производительности
Краткий анализ, бенчмарк: 100000 случайных чисел, один поток
1,Random: 2 мс
2,ThreadLocalRandom: 1 мс
3.SecureRandom
1) Алгоритм по умолчанию, SHAR1PRNG: около 80 мс.
2) NativePRNG: около 90 мс.
4.SplittableRandom: 1 мс
End
Нет проблем использовать Random в обычном режиме или в большинстве случаев, и он также является потокобезопасным. SplittableRandom — это класс, используемый для внутреннего использования и имеющий меньшее количество применений. ThreadLocalRandom позволяет сэкономить немного времени в среде с высокой степенью параллелизма и рекомендуется для приложений, стремящихся к производительности. А для тех, у кого есть потребности в безопасности, есть надежда, что更随机
Для некоторых лучше использовать SecureRandom.
В jdk так много генераторов случайных чисел. Вы много ели? Я все равно стоял на коленях.
Больше отличных статей.
«Микросервисы — это не все, а лишь подмножество определенного домена».
С таким количеством компонентов мониторинга всегда найдется подходящий для вас
«Наиболее часто используемый набор навыков «vim» в производственной среде Linux.