CountDownLatch ve Semafor


95

Kullanmanın herhangi bir avantajı var mı

java.util.concurrent.CountdownLatch

onun yerine

java.util.concurrent.Semaphore ?

Anlayabildiğim kadarıyla aşağıdaki parçalar neredeyse eşdeğer:

1. Semafor

final Semaphore sem = new Semaphore(0);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        sem.release();
      }
    }
  };
  t.start();
}

sem.acquire(num_threads);

2: CountDownLatch

final CountDownLatch latch = new CountDownLatch(num_threads);
for (int i = 0; i < num_threads; ++ i)
{
  Thread t = new Thread() {
    public void run()
    {
      try
      {
        doStuff();
      }
      finally
      {
        latch.countDown();
      }
    }
  };
  t.start();
}

latch.await();

2 numaralı durumda, mandal yeniden kullanılamaz ve daha da önemlisi, kaç iş parçacığı oluşturulacağını önceden bilmeniz gerekir (veya mandalı oluşturmadan önce tümünün başlatılmasını bekleyin.)

Peki hangi durumda mandal tercih edilebilir?

Yanıtlar:


112

CountDownLatchgenellikle sizin örneğinizin tam tersi için kullanılır. Genel olarak, await()countown sıfıra ulaştığında hepsi aynı anda başlayacak olan birçok iş parçacığına sahip olursunuz.

final CountDownLatch countdown = new CountDownLatch(1);

for (int i = 0; i < 10; ++ i) {
   Thread racecar = new Thread() {    
      public void run() {
         countdown.await(); //all threads waiting
         System.out.println("Vroom!");
      }
   };
   racecar.start();
}
System.out.println("Go");
countdown.countDown();   //all threads start now!

Bunu ayrıca, tüm iş parçacıklarının devam etmeden önce diğer iş parçacıklarının belirli bir noktaya yetişmesini beklemesine neden olan MPI tarzı bir "bariyer" olarak da kullanabilirsiniz.

final CountDownLatch countdown = new CountDownLatch(num_thread);

for (int i = 0; i < num_thread; ++ i) {
   Thread t= new Thread() {    
      public void run() {
         doSomething();
         countdown.countDown();
         System.out.printf("Waiting on %d other threads.",countdown.getCount());
         countdown.await();     //waits until everyone reaches this point
         finish();
      }
   };
   t.start();
}

Hepsi söylendi, CountDownLatchörneğinizde gösterdiğiniz şekilde güvenle kullanılabilir.


1
Teşekkürler. Bu yüzden, birden fazla evre mandalı bekleyebilirse iki örneğim eşdeğer olmaz ... sem.acquire (num_threads); ardından sem.release (num_threads) gelir ;? Bence bu onları tekrar eşdeğer kılar.
finnw

Bir anlamda, evet, aranan her ileti dizisi ardından serbest bırakıldığı sürece. Kesinlikle hayır. Bir mandalla, tüm iş parçacıkları aynı anda başlamaya uygundur. Semafor ile, birbiri ardına uygun hale gelirler (bu, farklı iş parçacığı zamanlamasına neden olabilir).
James Schek

Java dokümantasyonu, bir CountdownLatch'in örneğine uygun olduğunu ima ediyor gibi görünüyor: docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/… . Özellikle, "N olarak başlatılan bir CountDownLatch, bir iş parçacığının N iş parçacığı bir eylemi tamamlamasını veya bazı eylemi N kez tamamlamasını beklemek için kullanılabilir."
Chris Morris

Haklısın. Bunların CountDownLatch'in gördüğüm en yaygın kullanımları ve amaçlanan kullanım olduğunu yansıtmak için cevabımı biraz güncelleyeceğim.
James Schek

11
Bu, şu soruyu yanıtlar: CountDownLatch en sık kullanılan nedir? Bir Semafora göre CountDownLatch kullanmanın avantajları / farklılıkları ile ilgili orijinal soruya cevap vermez.
Marco Lackovic

68

CountDownLatch , bir dizi iş parçacığı başlatmak ve ardından tümü tamamlanana kadar (veya countDown()belirli bir sayıda çağrı yapana kadar) beklemek için kullanılır .

Semafor, bir kaynak kullanan eşzamanlı iş parçacığı sayısını kontrol etmek için kullanılır. Bu kaynak bir dosya gibi bir şey olabilir veya çalıştırılan iş parçacığı sayısını sınırlayarak cpu olabilir. Bir Semafordaki sayı, farklı iş parçacığı acquire()ve release().

Örnekte, aslında Kont bir tür Semaphore kullanıyoruz YUKARI Sürgüsüyle. Niyetinizin tüm konuların bitmesini beklemek olduğu göz önüne alındığında, kullanmak CountdownLatchniyetinizi daha net hale getirir.


24

Kısa özet:

  1. Semaphoreve CountDownLatchfarklı amaca hizmet eder.

  2. Kaynağa Semaphoreiş parçacığı erişimini kontrol etmek için kullanın .

  3. CountDownLatchTüm iş parçacıklarının tamamlanmasını beklemek için kullanın

Semaphore Javadocs'tan tanım:

A Semaphore, bir dizi izne sahiptir. acquire()Bir izin mevcut olana kadar gerekirse her blok ve ardından onu alır. Her biri release()bir izin ekler ve potansiyel olarak bir engelleyici ediniciyi serbest bırakır.

Ancak, hiçbir gerçek izin nesnesi kullanılmaz; SemaphoreSadece mevcut sayımını tutar ve ona göre davranır.

O nasıl çalışır?

Semaforlar, bir kaynağı kullanan eşzamanlı iş parçacığı sayısını kontrol etmek için kullanılır.Bu kaynak, paylaşılan bir veri veya bir kod bloğu ( kritik bölüm ) veya herhangi bir dosya gibi bir şey olabilir .

Bir sayıya göre Semaphoreçıkıp farklı ipler dediğimiz gibi aşağı olabilir acquire()ve release(). Ancak herhangi bir noktada, Semafor sayısından daha fazla sayıda iş parçacığına sahip olamazsınız.

Semaphore Kullanım durumları:

  1. Diske eşzamanlı erişimi sınırlama (bu, rakip disk aramaları nedeniyle performansı öldürebilir)
  2. Konu oluşturma sınırlaması
  3. JDBC bağlantı havuzu oluşturma / sınırlama
  4. Ağ bağlantısı kısıtlama
  5. CPU veya bellek yoğun görevleri kısma

Semafor kullanımları için bu makaleye bir göz atın .

CountDownLatch Javadocs'tan tanım:

Bir veya daha fazla iş parçacığının diğer iş parçacıklarında gerçekleştirilen bir dizi işlem tamamlanana kadar beklemesine izin veren bir senkronizasyon yardımı.

O nasıl çalışır?

CountDownLatchiş parçacığı sayısıyla başlatılan bir sayaca sahip olarak çalışır ve bu, bir iş parçacığının yürütülmesini her tamamladığında azaltılır. Count sıfıra ulaştığında, tüm iş parçacığı yürütmeyi tamamlamış demektir ve mandalda bekleyen iş parçacığı yürütmeyi sürdürür.

CountDownLatch Kullanım durumları:

  1. Maksimum Paralelliği Elde Etme: Bazen maksimum paralellik elde etmek için aynı anda birkaç iş parçacığı başlatmak isteriz
  2. Yürütmeye başlamadan önce N iş parçacığının tamamlanmasını bekleyin
  3. Kilitlenme tespiti.

Kavramları net bir şekilde anlamak için bu makaleye bir göz atın CountDownLatch.

Bu makaledeki Fork Join Pool'a da bir göz atın . İle bazı benzerlikleri vardır .CountDownLatch


7

Diyelim ki dörtlü bir golfçü bulma umuduyla golf profesyonelleri mağazasına girdiniz.

Profesyonel mağaza görevlilerinden birinden çay saati almak için sırada beklediğinizde, esasen aradınız proshopVendorSemaphore.acquire(), bir çay saati aldığınızda, proshopVendorSemaphore.release()aradınız. Not: Ücretsiz görevlilerden herhangi biri size hizmet edebilir, yani paylaşılan kaynak.

Şimdi başlangıç ​​noktasına yürüdünüz, o başlattı CountDownLatch(4)ve await()başkalarını beklemek için aradı, yani check-in dediğiniz bölümünüz için CountDownLatch. countDown()ve dörtlünün geri kalanı da öyle. Hepsi geldiğinde, marş devam eder ( await()çağrı geri döner)

Şimdi, dokuz delikten sonra, her biriniz ara verdiğinizde, varsayımsal olarak tekrar marşı dahil edelim, CountDownLatch(4)Delik 10'u açmak için Delik 1 ile aynı bekleme / senkronizasyon için 'yeni' kullanır .

Bununla birlikte, marş motoru CyclicBarrierbaşlamak için a kullanmışsa , aynı örneği Delik 10'da & atma kullanan ikinci bir mandal yerine sıfırlayabilirdi.


1
Cevabınızı anladığımdan emin değilim, ancak CountdownLatch ve Semaphore'un nasıl çalıştığını açıklamaya çalışıyorsanız, sorunun konusu bu değildir.
finnw

10
Ne yazık ki golf hakkında hiçbir şey bilmiyorum.
yüzük taşıyıcısı

ancak başlangıç ​​işleri .acquire (oyuncular) ile yapılabilir ve sürümle birlikte relesed sayısını artırabilir. geri sayım mandalı daha az işlevselliğe sahip ve yeniden kullanılabilirlik yok gibi görünüyor.
Lassi Kinnunen

1

Ücretsiz olarak temin edilebilen kaynağa bakıldığında, iki sınıfın uygulanmasında sihir yoktur, bu nedenle performansları hemen hemen aynı olmalıdır. Niyetinizi daha açık hale getiren birini seçin.


0

CountdownLatchawait()sayım sıfıra ulaşana kadar iş parçacıkları yöntemde bekletir. Belki de tüm iş parçacıklarınızın bir şeyin 3 çağrısına kadar beklemesini istersiniz, o zaman tüm ileti dizileri gidebilir. Bir Latchgenel olarak sıfırlanamaz.

A Semaphore, iş parçacıklarının izinleri almasına izin verir, bu da çok fazla iş parçacığının aynı anda yürütülmesini engeller, devam etmek için gereken izinleri alamazsa engeller. İzinler Semaphore, diğer bekleyen iş parçacığının devam etmesine izin vermek için iade edilebilir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.