CountDownLatch vs. Semaphore

95

Apakah ada keuntungan menggunakan

java.util.concurrent.CountdownLatch

dari pada

java.util.concurrent.Semaphore ?

Sejauh yang saya tahu, fragmen berikut hampir setara:

1. Semaphore

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

Kecuali jika dalam kasus # 2 kait tidak dapat digunakan kembali dan yang lebih penting Anda perlu mengetahui sebelumnya berapa banyak utas yang akan dibuat (atau tunggu sampai semuanya dimulai sebelum membuat kait.)

Jadi, dalam situasi apa gerendel lebih disukai?

finnw
sumber

Jawaban:

112

CountDownLatchsering digunakan untuk kebalikan dari contoh Anda. Secara umum, Anda akan memiliki banyak utas yang memblokir await()itu semua akan dimulai secara bersamaan ketika hitungan mundur mencapai nol.

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!

Anda juga dapat menggunakan ini sebagai "penghalang" bergaya MPI yang menyebabkan semua utas menunggu utas lain mengejar ke titik tertentu sebelum melanjutkan.

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

Meskipun demikian, CountDownLatchdapat digunakan dengan aman seperti yang Anda tunjukkan dalam contoh Anda.

James Schek
sumber
1
Terima kasih. Jadi dua contoh saya tidak akan setara jika beberapa utas bisa menunggu di latch ... kecuali sem.acquire (num_threads); diikuti oleh sem.release (num_threads) ;? Saya pikir itu akan membuat mereka setara lagi.
finnw
Dalam arti, ya, asalkan setiap utas yang disebut memperoleh diikuti oleh rilis. Sebenarnya, tidak. Dengan kait, semua utas memenuhi syarat untuk memulai secara bersamaan. Dengan semaphore, mereka menjadi memenuhi syarat satu demi satu (yang dapat menghasilkan penjadwalan utas yang berbeda).
James Schek
Dokumentasi Java tampaknya menyiratkan bahwa CountdownLatch cocok dengan contohnya: docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/… . Secara khusus, "CountDownLatch yang diinisialisasi ke N dapat digunakan untuk membuat satu utas menunggu hingga N utas menyelesaikan beberapa tindakan, atau beberapa tindakan telah diselesaikan N kali."
Chris Morris
Kamu benar. Saya akan memperbarui jawaban saya sedikit untuk mencerminkan bahwa ini adalah penggunaan paling umum dari CountDownLatch yang pernah saya lihat vs bahwa itu adalah penggunaan yang dimaksudkan.
James Schek
11
Ini menjawab pertanyaan Apa yang paling sering digunakan dari CountDownLatch? Itu tidak menjawab pertanyaan asli tentang keuntungan / perbedaan menggunakan CountDownLatch melalui Semaphore.
Marco Lackovic
67

CountDownLatch digunakan untuk memulai serangkaian utas dan kemudian menunggu sampai semuanya selesai (atau sampai mereka memanggil countDown()beberapa kali.

Semaphore digunakan untuk mengontrol jumlah thread bersamaan yang menggunakan sumber daya. Sumber daya itu bisa berupa file, atau bisa jadi cpu dengan membatasi jumlah utas yang dieksekusi. Hitungan Semaphore bisa naik dan turun sebagai thread yang berbeda memanggil acquire()dan release().

Dalam contoh Anda, Anda pada dasarnya menggunakan Semaphore sebagai semacam Count UP Latch. Mengingat bahwa niat Anda adalah menunggu semua utas selesai, menggunakan CountdownLatchmembuat niat Anda lebih jelas.

mtruesdell.dll
sumber
23

Ringkasan singkat:

  1. Semaphoredan CountDownLatchmelayani tujuan yang berbeda.

  2. Gunakan Semaphoreuntuk mengontrol akses utas ke sumber daya.

  3. Gunakan CountDownLatchuntuk menunggu penyelesaian semua utas

Semaphore definisi dari Javadocs:

A Semaphorememiliki satu set izin. Setiap acquire()blok jika perlu sampai izin tersedia, dan kemudian mengambilnya. Masing-masing release()menambahkan izin, berpotensi melepaskan pengakuisisi pemblokiran.

Namun, sebenarnya tidak ada objek izin yang digunakan; yang Semaphorehanya menyimpan hitungan jumlah yang tersedia dan bertindak sesuai.

Bagaimana cara kerjanya?

Semaphore digunakan untuk mengontrol jumlah thread bersamaan yang menggunakan resource. Resource tersebut dapat berupa data bersama, atau blok kode ( bagian kritis ) atau file apa pun.

Hitungan Semaphoredapat naik dan turun sebagai utas yang berbeda memanggil acquire()dan release(). Tetapi pada titik waktu mana pun, Anda tidak dapat memiliki lebih banyak jumlah utas lebih dari jumlah Semaphore.

Semaphore Kasus penggunaan:

  1. Membatasi akses bersamaan ke disk (ini dapat mematikan kinerja karena persaingan pencarian disk)
  2. Pembatasan pembuatan benang
  3. Penyatuan / pembatasan koneksi JDBC
  4. Pelambatan koneksi jaringan
  5. Menghambat tugas CPU atau memori yang intensif

Lihat artikel ini untuk penggunaan semafor.

CountDownLatch definisi dari Javadocs:

Bantuan sinkronisasi yang memungkinkan satu atau lebih utas menunggu hingga serangkaian operasi dilakukan di utas lain selesai.

Bagaimana cara kerjanya?

CountDownLatchbekerja dengan memiliki penghitung yang diinisialisasi dengan jumlah utas, yang berkurang setiap kali utas menyelesaikan eksekusinya. Saat hitungan mencapai nol, itu berarti semua utas telah menyelesaikan eksekusinya, dan utas yang menunggu di kait melanjutkan eksekusi.

CountDownLatch Kasus penggunaan:

  1. Mencapai Paralelisme Maksimum: Terkadang kami ingin memulai sejumlah utas pada saat yang sama untuk mencapai paralelisme maksimum
  2. Tunggu N utas selesai sebelum memulai eksekusi
  3. Deteksi kebuntuan.

Lihat artikel ini untuk memahami CountDownLatchkonsep dengan jelas.

Lihat juga Fork Join Pool di artikel ini . Ini memiliki beberapa kesamaan dengan CountDownLatch.

Ravindra babu
sumber
7

Katakanlah Anda masuk ke toko profesional golf, berharap menemukan berempat,

Ketika Anda mengantre untuk mendapatkan waktu tee dari salah satu petugas toko profesional, pada dasarnya Anda menelepon proshopVendorSemaphore.acquire(), begitu Anda mendapatkan waktu tee, Anda menelepon. proshopVendorSemaphore.release()Catatan: salah satu petugas gratis dapat melayani Anda, yaitu sumber daya bersama.

Sekarang Anda berjalan ke starter, dia memulai CountDownLatch(4)dan memanggil await()untuk menunggu orang lain, untuk bagian Anda, Anda menelepon check-in yaitu CountDownLatch. countDown()dan begitu pula yang berempat. Ketika semua tiba, starter memberi pergi ( await()panggilan kembali)

Sekarang, setelah sembilan hole ketika Anda masing-masing mengambil break, secara hipotetis melibatkan starter lagi, dia menggunakan 'baru' CountDownLatch(4)untuk tee off Hole 10, wait / sync yang sama seperti Hole 1.

Namun, jika starter menggunakan a CyclicBarrieruntuk memulai, dia dapat mengatur ulang instance yang sama di Lubang 10 alih-alih kait kedua, yang menggunakan & melempar.

Raj Srinivas
sumber
1
Saya tidak yakin saya memahami jawaban Anda, tetapi jika Anda mencoba menjelaskan cara kerja CountdownLatch dan Semaphore, itu bukan subjek pertanyaannya.
finnw
10
Sayangnya saya tidak tahu apa-apa tentang golf.
pembawa cincin
tetapi hal-hal awal bisa dilakukan dengan baik dengan .acquire (pemain) dan meningkatkan jumlah terkait dengan rilis. countdownlatch tampaknya hanya memiliki lebih sedikit fungsi dan tidak dapat digunakan kembali.
Lassi Kinnunen
1

Melihat sumber yang tersedia secara gratis, tidak ada keajaiban dalam implementasi kedua kelas tersebut, jadi kinerjanya harus hampir sama. Pilih salah satu yang membuat niat Anda lebih jelas.

Tom Hawtin - tackline
sumber
0

CountdownLatchmembuat utas menunggu pada await()metode, sampai saat hitungan telah mencapai nol. Jadi mungkin Anda ingin semua utas menunggu hingga 3 pemanggilan sesuatu, lalu semua utas bisa masuk. A Latchumumnya tidak dapat diatur ulang.

A Semaphoremengizinkan utas untuk mengambil izin, yang mencegah terlalu banyak utas dijalankan sekaligus, memblokir jika tidak bisa mendapatkan izin yang diperlukan untuk melanjutkan. Izin dapat dikembalikan ke Semaphorememungkinkan utas menunggu lainnya untuk melanjutkan.

Spencer Kormos
sumber