2015-08-20 20 views
11

Czy można bezpiecznie używać tej samej instancji Random do generowania strumienia (lub strumienia równoległego) i wpływania na ten strumień w jednej z jego części?IntStream z losowej i losowej współbieżności

Weź pod uwagę poniższy kod. Ten sam gen jest używany do generowania równoległego IntStream i generowania losowej przestrzeni co kilka znaków. Działa i kończy się pomyślnie, żaden wyjątek nie został zgłoszony.

Ale czy ten wątek kodu jest bezpieczny? Wygląda na to, ponieważ nie ma niepoprawnych (poza zakresem) wartości znaków. Myślę, że powinienem uszkodzić wewnętrzne dane Random, ponieważ jego metody nie są oznaczone jako synchronized, ale najwyraźniej tak nie jest. Czemu?

public class RandomGenTest { 

    Random gen = new Random(); 

    String getRandomText(int len, double spaceProb) { 
     return gen.ints(len, 'a', 'z'+1) 
        .map(i-> gen.nextDouble()<spaceProb?' ':i) 
        .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString(); 
    } 

    @Test 
    public void test() { 
     for (int a=10000; a<10000000; a*=2) { 
      String text = getRandomText(a, .2); 
      Assert.assertTrue(text.chars().allMatch(c -> (c>='a' && c<='z') || c==' ')); 
     } 
    } 

} 
+5

Należy zauważyć, że użycie 'ograniczenia' w strumieniu równoległym jest receptą na awarię wydajności. I to jest niepotrzebne tutaj, ponieważ możesz użyć ['gen.ints (len, 'a', 'z')'] (http://docs.oracle.com/javase/8/docs/api/java/util/ Random.html # ints-long-int-int-) do skonstruowania skończonego strumienia w pierwszej kolejności. Co więcej, jawne tworzenie nowych obiektów za pomocą 'nowej postaci' jest niepotrzebne, możesz pozwolić, aby autoboxing zrobił coś słusznego. Ale kiedy zmienisz pierwszy 'StringBuilder :: append' na' StringBuilder :: appendCodePoint', nie potrzebujesz w ogóle boxingu, ale możesz po prostu wykonać wszystko używając 'IntStream'. – Holger

+4

Nawiasem mówiąc, granica jest * wyłączna *, więc jeśli chcesz włączyć '' z'', musisz użyć '' z '+ 1' jako związanego. → 'gen.int (len, 'a', 'z' + 1) .parallel(). Map (i -> gen.nextDouble() Holger

+2

Przy okazji, możesz również uprościć swoje twierdzenie:' Assert.assertTrue (text.chars(). allMatch (c -> (c> = 'a' && c <= 'z') || c == '')); 'który jest taki sam jak' Assert.assertTrue (text.matches ("[az] *")); '(z spacją po' z ') – Holger

Odpowiedz

8

Random „s Javadoc czary to działa:

Przypadki java.util.Random są threadsafe. Jednak jednoczesne korzystanie z tej samej instancji java.util.Random w wątkach może napotkać rywalizację i wynikającą z tego niską wydajność. Zamiast tego należy rozważyć użycie ThreadLocalRandom w projektach wielowątkowych.

Random jest bezpieczny wątku przedmiot z racji na AtomicLong który utrzymuje bieżący nasienie, więc stosując go odwraca większość równoległego SpeedUp który jest punktem ćwiczeń.

Zamiast tego użyj ThreadLocalRandom.getCurrent() i unikaj co najmniej problemu z rywalizacją (chociaż wprowadzono obciążenie związane z wyszukiwaniem ThreadLocal). Użyj także SplittableRandom, aby pobrać zewnętrzny strumień liczb losowych. Ta implementacja umożliwia swobodny dostęp do elementów strumieniowych, co jest kluczem do zrównoleglenia.

import static java.util.concurrent.ThreadLocalRandom.current; 

String getRandomText(int len, double spaceProb) { 
    return new SplittableRandom().ints(len, 'a', 'z'+1).parallel() 
     .map(i -> current().nextDouble()<spaceProb ? ' ' : i) 
     .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append).toString(); 
Powiązane problemy