Java CyclicBarrier vs CountDownLatch

1. Perkenalan

Dalam tutorial ini, kita akan membandingkan CyclicBarrier dan CountDownLatch dan mencoba untuk memahami persamaan dan perbedaan antara keduanya.

2. Apakah Ini?

Dalam hal konkurensi, mungkin sulit untuk membuat konsep apa yang ingin dicapai masing-masing.

Pertama dan terpenting, baik CountDownLatch dan CyclicBarrier digunakan untuk mengelola aplikasi multi-threaded .

Dan, keduanya dimaksudkan untuk mengungkapkan bagaimana utas atau sekelompok utas tertentu harus menunggu.

2.1. CountDownLatch

Sebuah CountDownLatch adalah membangun yang thread menunggu s sementara benang lain menghitung mundur pada kait hingga mencapai nol.

Kita dapat menganggap ini seperti hidangan di restoran yang sedang disiapkan. Tidak peduli juru masak mana yang menyiapkan berapa banyak n item, pelayan harus menunggu sampai semua item ada di piring. Jika piring mengambil n item, juru masak mana pun akan menghitung mundur kait untuk setiap item yang dia taruh di piring.

2.2. CyclicBarrier

Sebuah CyclicBarrier adalah membangun dapat digunakan kembali di mana sekelompok benang menunggu bersama-sama sampai semua benang tiba . Pada titik itu, penghalang dipatahkan dan tindakan opsional dapat diambil.

Kita dapat menganggap ini seperti sekelompok teman. Setiap kali mereka berencana untuk makan di restoran, mereka memutuskan tempat pertemuan bersama. Mereka menunggu satu sama lain di sana, dan hanya ketika semua orang tiba mereka bisa pergi ke restoran untuk makan bersama.

2.3. Bacaan lebih lanjut

Dan untuk lebih banyak detail pada masing-masing individu, lihat tutorial kami sebelumnya pada CountDownLatch dan CyclicBarrier masing-masing.

3. Tugas vs. Untaian

Mari selami lebih dalam beberapa perbedaan semantik antara kedua kelas ini.

Seperti yang dinyatakan dalam definisi, CyclicBarrier memungkinkan sejumlah utas menunggu satu sama lain, sedangkan CountDownLatch memungkinkan satu atau lebih utas menunggu sejumlah tugas selesai.

Singkatnya, CyclicBarrier mempertahankan jumlah utas sedangkan CountDownLatch mempertahankan jumlah tugas .

Dalam kode berikut, kami mendefinisikan CountDownLatch dengan hitungan dua. Selanjutnya, kami memanggil countDown () dua kali dari satu utas:

CountDownLatch countDownLatch = new CountDownLatch(2); Thread t = new Thread(() -> { countDownLatch.countDown(); countDownLatch.countDown(); }); t.start(); countDownLatch.await(); assertEquals(0, countDownLatch.getCount());

Setelah kait mencapai nol, panggilan untuk menunggu kembali.

Perhatikan bahwa dalam kasus ini, kami dapat membuat utas yang sama mengurangi jumlah dua kali.

CyclicBarrier berbeda dalam hal ini.

Mirip dengan contoh di atas, kami membuat CyclicBarrier, sekali lagi dengan hitungan dua dan memanggil await () di atasnya, kali ini dari utas yang sama:

CyclicBarrier cyclicBarrier = new CyclicBarrier(2); Thread t = new Thread(() -> { try { cyclicBarrier.await(); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { // error handling } }); t.start(); assertEquals(1, cyclicBarrier.getNumberWaiting()); assertFalse(cyclicBarrier.isBroken());

Perbedaan pertama di sini adalah bahwa benang-benang yang menunggu itu sendirilah penghalang.

Kedua, dan yang lebih penting, await () kedua tidak berguna . Seutas benang tidak dapat menghitung mundur penghalang dua kali.

Memang, karena t harus menunggu untuk thread lain untuk memanggil Tunggulah, () - untuk membawa menghitung dua - t 's panggilan kedua untuk Tunggulah, () tidak akan benar-benar dipanggil sampai penghalang sudah rusak!

Dalam pengujian kami, penghalang belum dilintasi karena kami hanya memiliki satu utas yang menunggu dan bukan dua utas yang akan diperlukan agar penghalang tersandung. Ini juga terbukti dari metode cyclicBarrier.isBroken () , yang mengembalikan false .

4. Dapat digunakan kembali

Perbedaan paling jelas kedua antara kedua kelas ini adalah dapat digunakan kembali. Untuk menguraikan, saat penghalang berjalan di CyclicBarrier , hitungan disetel ulang ke nilai aslinya. CountDownLatch berbeda karena hitungan tidak pernah disetel ulang.

Dalam kode yang diberikan, kami mendefinisikan CountDownLatch dengan hitungan 7 dan menghitungnya melalui 20 panggilan berbeda:

CountDownLatch countDownLatch = new CountDownLatch(7); ExecutorService es = Executors.newFixedThreadPool(20); for (int i = 0; i  { long prevValue = countDownLatch.getCount(); countDownLatch.countDown(); if (countDownLatch.getCount() != prevValue) { outputScraper.add("Count Updated"); } }); } es.shutdown(); assertTrue(outputScraper.size() <= 7);

Kami mengamati bahwa meskipun 20 utas berbeda memanggil countDown () , hitungan tidak disetel ulang setelah mencapai nol.

Mirip dengan contoh di atas, kami mendefinisikan CyclicBarrier dengan hitungan 7 dan menunggu dari 20 utas yang berbeda:

CyclicBarrier cyclicBarrier = new CyclicBarrier(7); ExecutorService es = Executors.newFixedThreadPool(20); for (int i = 0; i  { try { if (cyclicBarrier.getNumberWaiting()  7);

Dalam hal ini, kami mengamati bahwa nilai menurun setiap kali utas baru berjalan, dengan mengatur ulang ke nilai asli, setelah mencapai nol.

5. Kesimpulan

Semua dalam semua, CyclicBarrier dan CountDownLatchkeduanya merupakan alat yang berguna untuk sinkronisasi antara beberapa utas. Namun, mereka pada dasarnya berbeda dalam hal fungsionalitas yang mereka sediakan. Pertimbangkan masing-masing dengan hati-hati saat menentukan mana yang tepat untuk pekerjaan itu.

Seperti biasa, semua contoh yang dibahas dapat diakses di Github.