Чи справедливо ділити один екземпляр Random
класу між декількома потоками? І зокрема зателефонувати nextInt(int)
з декількох потоків?
java.util.concurrent.ThreadLocalRandom
.
Чи справедливо ділити один екземпляр Random
класу між декількома потоками? І зокрема зателефонувати nextInt(int)
з декількох потоків?
java.util.concurrent.ThreadLocalRandom
.
Відповіді:
Це безпечно для потоків, в тому сенсі, що він все одно генерує випадкові числа при використанні кількох потоків.
Реалізація JVM Sun / Oracle використовує синхронізовані та AtomicLong в якості насіння для поліпшення послідовності між нитками. Але, схоже, це не гарантується на всіх платформах документації.
Я б не писав вашій програмі, щоб вимагати такої гарантії, тим більше, що ви не можете визначити порядок, в який nextInt()
буде викликатись.
Це безпечно для ниток, хоча це було не завжди.
Докладнішу інформацію див. У розділі http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6362070 .
Згідно з документацією, Math.random () гарантує, що вона безпечна для використання в декількох потоках . Але клас Random цього не робить. Я б припустив, що тоді вам доведеться синхронізувати це самостійно.
Так, Random є безпечним для потоків. nextInt()
метод викликає захищений next(int)
метод , який використовує AtomicLong seed, nextseed
(атомний довгий) для створення наступного насіння. AtomicLong
застосовується для безпеки ниток при генерації насіння.
Як було сказано, це збереження ниток, але це може бути розумно використовувати java.util.concurrent.ThreadLocalRandom
відповідно до цієї статті (посилання мертве). ThreadLocalRandom також є підкласом Random, тому він сумісний ззаду назад.
У статті пов'язані між собою порівнюваних результати різних класів випадкових профілювання:
java.util.Random
,java.util.concurrent.ThreadLocalRandom
іjava.lang.ThreadLocal<java.util.Random>
. Результати показали, що використання ThreadLocalRandom є найбільш ефективним, за ним слідує ThreadLocal та найгірше виконання Random.
Немає причини, що кілька потоків не можуть використовувати один і той же Random. Однак, оскільки клас явно не є безпечним для потоків і підтримує послідовність псевдовипадкових чисел через насіння. Кілька ниток можуть закінчуватися одним і тим же випадковим числом. Було б краще створити декілька Randoms для кожної нитки та посіяти їх по-різному.
EDIT : Я щойно помітив, що впровадження Sun використовує AtomicLong, тому я гадаю, що це безпечно для потоків (як також зазначив Пітер Лоурі (+1)).
EDIT2 : OpenJDK також використовує AtomicLong для насіння. Як казали інші, хоча на це все ще недобре покладатися.
Ось як я вирішив проблему, не припускаючи, що Random використовує атомні змінні. Він все ще може випадково стикатися, якщо currentTime * thread id
в майбутньому дорівнює деякий час, але це досить рідко для моїх потреб. Щоб по-справжньому уникнути можливості зіткнень, ви можете з кожним запитом чекати унікальної часової позначки годинника.
/**
* Thread-specific random number generators. Each is seeded with the thread
* ID, so the sequence of pseudo-random numbers are unique between threads.
*/
private static ThreadLocal<Random> random = new ThreadLocal<Random>() {
@Override
protected Random initialValue() {
return new Random(
System.currentTimeMillis() *
Thread.currentThread().getId());
}
};
(24*60*60*1000)
значна частина?
(24*60*60*1000)
Було так , що потік з ідентифікатором 12
вxxxxxxxxxx045
Millis не отримав висівають таким же , як нитка 22
в xxxxxxxxxx035
Millis. Однак я не маю жодних вагомих причин вважати, що ідентифікатори потоків наростають, і немає жодної вагомої причини думати, що я створюю теми в більш випадкові часи завтра, ніж сьогодні. Я спростив alg зараз і оновив опис, щоб визначити недолік.
Random
Клас не налаштований для одного примірника , які будуть використовуватися в декількох потоках. Звичайно, якщо ви зробили це, швидше за все, ви збільшите можливість отримати непередбачувані та наблизитись до випадкових чисел. Але оскільки це псевдовипадковий генератор, я не можу зрозуміти, чому вам потрібно поділитися екземпляром. Чи є більш конкретна вимога?