2015-08-20 35 views
11

Bir akış (veya paralel akış) oluşturmak ve bu akışını parçalarından birinde etkilemek için aynı Random örneğini kullanmak güvenli midir?Rastgele ve Rastgele eşzamanlılıktan IntStream

Aşağıdaki kodu dikkate alın. Aynı gen, paralel bir IntStream oluşturmak ve her birkaç karakterde rastgele bir alan oluşturmak için kullanılır. Başarılı bir şekilde çalışır ve tamamlanır, hiçbir istisna atılmaz.

Bu kod parçacığı güvenli midir? Görünen o ki, geçersiz (aralık dışı) karakter değerleri yoktur. Yöntemleri synchronized olarak işaretlenmediğinden, Random'un iç verilerini bozmaya çalışmalıyım, ama görünüşe göre bu durum böyle değil. Niye ya?

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

Paralel akışta 'limit'i kullanmanın bir performans felaketi için bir reçete olduğunu unutmayın. Ve burada gereksizdir, çünkü ['gen.ints (len, 'a', 'z')'] (http://docs.oracle.com/javase/8/docs/api/java/util/ Random.html # ints-long-int-int-) ilk etapta sonlu bir akış oluşturmak için. Ayrıca, yeni nesnelerin 'Yeni Karakter' aracılığıyla açık bir şekilde oluşturulması gereksizdir, otomatik kutlamanın doğru şeyi yapmasına izin verebilirsiniz. Ancak, ilk StringBuilder :: append 'dizgesini' StringBuilder :: appendCodePoint' olarak değiştirdiğinizde, hiç kutu kullanmanıza gerek yoktur, ancak 'IntStream' kullanarak her şeyi gerçekleştirebilirsiniz. – Holger

+4

Bu arada, bağlı * özel *, yani 'z' dahil edilmek isteniyorsa, 'z' + 1 'öğesini bağlı olarak kullanmanız gerekir. → 'gen.ints (len, 'a', 'z' + 1) .parallel() haritası (i -> gen.nextDouble() Holger

+2

Bu arada, aşağıdakileri de basitleştirebilirsiniz:“ Assert.assertTrue (text.chars(). allMatch (c -> (c> = 'a' &&) c <= 'z') || c == '')); 'Assert.assertTrue (text.matches (" [az] * ")) ile aynıdır; ') – Holger

cevap

8

Random 'ın Javadoc büyü dışarı: java.util.Random ait

Örnekleri ÅŸan bulunmaktadır. Ancak, aynı java.util.Random örneğinin iş parçacıklarının aynı anda kullanımı, çekişme ve sonuç olarak düşük performansla karşılaşabilir. Bunun yerine çok parçalı tasarımlarda ThreadLocalRandom'ı kullanmayı düşünün.

Random

yüzden sizin egzersiz noktasıdır paralel speedup çoğunu tersine uygulayarak geçerli tohum tutan bir AtomicLong sayesinde bir iş parçacığı güvenli bir nesnedir. Bunun yerine ThreadLocalRandom.getCurrent() kullanın ve en az çekişme sorununu önleyin (her ne kadar ThreadLocal genel bakışının ekini sunsa da). Ayrıca rastgele sayıların dış akışını almak için SplittableRandom'u kullanın. Bu uygulama, goood paralelleştirilebilirliğinin anahtarı olan akış elemanlarına rastgele erişim sağlar.

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();