В нашем проекте для одной задачи мы использовали статический случайный экземпляр для цели генерации случайных чисел. После выпуска Java 7 появился новый класс ThreadLocalRandom для генерации случайных чисел.ThreadLocalRandom с общим статическим экспериментом сравнения производительности случайных экземпляров
Из спецификации:
При необходимости, использование ThreadLocalRandom, а не совместно Случайные объекты в параллельных программах, как правило, сталкиваются с гораздо меньше накладных расходов и раздор. Использование ThreadLocalRandom особенно подходит, когда несколько задач (например, каждый из ForkJoinTask) используют случайные числа параллельно в пулах потоков.
, а также:
Когда все обычаи этой формы, то никогда нельзя случайно разделить ThreadLocalRandom между несколькими потоками.
Так что я сделал свой небольшой тест:
public class ThreadLocalRandomTest {
private static final int THREAD_COUNT = 100;
private static final int GENERATED_NUMBER_COUNT = 1000;
private static final int INT_RIGHT_BORDER = 5000;
private static final int EXPERIMENTS_COUNT = 5000;
public static void main(String[] args) throws InterruptedException {
System.out.println("Number of threads: " + THREAD_COUNT);
System.out.println("Length of generated numbers chain for each thread: " + GENERATED_NUMBER_COUNT);
System.out.println("Right border integer: " + INT_RIGHT_BORDER);
System.out.println("Count of experiments: " + EXPERIMENTS_COUNT);
int repeats = 0;
int workingTime = 0;
long startTime = 0;
long endTime = 0;
for (int i = 0; i < EXPERIMENTS_COUNT; i++) {
startTime = System.currentTimeMillis();
repeats += calculateRepeatsForSharedRandom();
endTime = System.currentTimeMillis();
workingTime += endTime - startTime;
}
System.out.println("Average repeats for shared Random instance: " + repeats/EXPERIMENTS_COUNT
+ ". Average working time: " + workingTime/EXPERIMENTS_COUNT + " ms.");
repeats = 0;
workingTime = 0;
for (int i = 0; i < EXPERIMENTS_COUNT; i++) {
startTime = System.currentTimeMillis();
repeats += calculateRepeatsForTheadLocalRandom();
endTime = System.currentTimeMillis();
workingTime += endTime - startTime;
}
System.out.println("Average repeats for ThreadLocalRandom: " + repeats/EXPERIMENTS_COUNT
+ ". Average working time: " + workingTime/EXPERIMENTS_COUNT + " ms.");
}
private static int calculateRepeatsForSharedRandom() throws InterruptedException {
final Random rand = new Random();
final Map<Integer, Integer> counts = new HashMap<>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = rand.nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
thread.start();
thread.join();
}
int repeats = 0;
for (Integer value : counts.values()) {
if (value > 1) {
repeats += value;
}
}
return repeats;
}
private static int calculateRepeatsForTheadLocalRandom() throws InterruptedException {
final Map<Integer, Integer> counts = new HashMap<>();
for (int i = 0; i < THREAD_COUNT; i++) {
Thread thread = new Thread() {
@Override
public void run() {
for (int j = 0; j < GENERATED_NUMBER_COUNT; j++) {
int random = ThreadLocalRandom.current().nextInt(INT_RIGHT_BORDER);
if (!counts.containsKey(random)) {
counts.put(random, 0);
}
counts.put(random, counts.get(random) + 1);
}
}
};
thread.start();
thread.join();
}
int repeats = 0;
for (Integer value : counts.values()) {
if (value > 1) {
repeats += value;
}
}
return repeats;
}
}
Я также добавил, тест на неразделяемую Random и получили следующие результаты:
Number of threads: 100
Length of generated numbers chain for each thread: 100
Right border integer: 5000
Count of experiments: 10000
Average repeats for non-shared Random instance: 8646. Average working time: 13 ms.
Average repeats for shared Random instance: 8646. Average working time: 13 ms.
Average repeats for ThreadLocalRandom: 8646. Average working time: 13 ms.
Что касается меня, это немного странно, я ожидал хотя бы увеличения скорости при использовании ThreadLocalRandom по сравнению с общим экземпляром Random, но не вижу разницы вообще.
Может кто-нибудь объяснить, почему это работает так, может быть, я ничего не понял. Заранее спасибо.
Позвольте мне попугать специалистов: микрообъектив трудно получить. Если вы хотите попробовать, используйте [Caliper] (https://code.google.com/p/caliper/), чтобы сделать это. –
ну, в первую очередь, ваши потоки не параллельны, когда вы «присоединяетесь» к нити сразу после запуска одного перед запуском другого. –
** намного меньше накладных расходов и утверждение ** не означает ** это быстрее ** это просто означает * при определенных типах и уровнях напряжения может быть улучшена пропускная способность и стабильность *. – OldCurmudgeon