Java CyclicBarrier vs CountDownLatch

1. Pengenalan

Dalam tutorial ini, kita akan membandingkan CyclicBarrier dan CountDownLatch dan cuba memahami persamaan dan perbezaan antara kedua-dua.

2. Apakah Ini?

Ketika bersamaan, sangat sukar untuk konsep apa yang ingin dicapai masing-masing.

Pertama sekali, kedua-dua CountDownLatch dan CyclicBarrier digunakan untuk menguruskan aplikasi multi-threaded .

Dan, kedua-duanya bertujuan untuk menyatakan bagaimana benang atau kumpulan utas tertentu harus menunggu.

2.1. CountDownLatch

A CountDownLatch adalah konstruk yang benang tunggu s pada ketika benang lain mengira detik di selak hingga mencapai sifar.

Kita boleh memikirkannya seperti hidangan di restoran yang sedang disediakan. Tidak kira yang memasak menyediakan bagaimanapun banyak n barang-barang, pelayan perlu menunggu sehingga semua item adalah dalam pinggan. Jika plat mengambil n barang-barang, apa-apa masak akan mengira detik di selak untuk setiap item dia meletakkan di atas pinggan.

2.2. CyclicBarrier

A CyclicBarrier adalah membina yang boleh digunakan semula di mana sekumpulan benang menunggu bersama-sama sehingga semua benang tiba . Pada ketika itu, penghalang sudah rosak dan tindakan boleh diambil secara pilihan.

Kita boleh memikirkannya seperti sekumpulan rakan. Setiap kali mereka merancang untuk makan di restoran, mereka akan menentukan tempat umum untuk bertemu. Mereka saling menunggu di sana, dan hanya apabila semua orang tiba mereka boleh pergi ke restoran untuk makan bersama.

2.3. Bacaan lanjut

Dan untuk lebih banyak detail pada masing-masing secara berasingan, serta merujuk kepada tutorial kami sebelum ini mengenai CountDownLatch dan CyclicBarrier masing-masing.

3. Tugasan vs Threads

Mari kita selami lebih mendalam mengenai beberapa perbezaan semantik antara kedua kelas ini.

Seperti yang dinyatakan dalam definisi, CyclicBarrier membenarkan sebilangan utas menunggu satu sama lain, sedangkan CountDownLatch membenarkan satu atau lebih utas menunggu beberapa tugas selesai.

Pendek kata, CyclicBarrier mengekalkan kiraan benang manakala CountDownLatch mengekalkan kiraan tugas .

Dalam kod berikut, kita menentukan CountDownLatch dengan kiraan dua. Seterusnya, 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 sifar, panggilan untuk menunggu kembali.

Perhatikan bahawa dalam kes ini, kami dapat membuat urutan yang sama mengurangkan kiraan dua kali.

Walau bagaimanapun , CyclicBarrier berbeza pada masa ini.

Sama dengan contoh di atas, kami membuat CyclicBarrier, sekali lagi dengan jumlah dua dan panggilan menunggu () 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());

Perbezaan pertama di sini adalah bahawa benang yang menunggu adalah penghalang mereka sendiri.

Kedua, dan yang lebih penting, penantian kedua () tidak berguna . Satu utas tidak boleh mengira penghalang dua kali.

Malah, kerana t perlu menunggu untuk thread lain untuk memanggil Menanti () - untuk membawa perhitungan ke dua - t 's panggilan kedua untuk Menanti () akan sebenarnya tidak dituntut sehingga halangan sudah rosak!

Dalam ujian kami, penghalang belum dilintasi kerana kami hanya mempunyai satu utas yang menunggu dan bukan dua utas yang diperlukan agar penghalang dilonggarkan. Ini juga dapat dilihat dari kaedah cyclicBarrier.isBroken () , yang mengembalikan false .

4. Kebolehgunaan semula

Perbezaan kedua yang paling jelas antara kedua kelas ini adalah penggunaan semula. Untuk dihuraikan, ketika penghalang bergerak di CyclicBarrier , kiraannya ditetapkan semula ke nilai asalnya. CountDownLatch berbeza kerana kiraan tidak pernah diset semula.

Dalam kod yang diberikan, kami menentukan CountDownLatch dengan kiraan 7 dan menghitungnya melalui 20 panggilan yang berbeza:

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 memerhatikan bahawa walaupun 20 utas yang berbeza memanggil countDown () , kiraannya tidak diset semula setelah mencapai sifar.

Sama seperti contoh di atas, kami menentukan CyclicBarrier dengan kiraan 7 dan tunggu dari 20 utas yang berbeza:

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

Dalam kes ini, kami melihat bahawa nilainya menurun setiap kali thread baru dijalankan, dengan menetapkan semula ke nilai asalnya, setelah mencapai sifar.

5. Kesimpulan

Semua dalam semua, CyclicBarrier dan CountDownLatchkedua-duanya merupakan alat yang berguna untuk penyegerakan antara pelbagai utas. Walau bagaimanapun, asasnya berbeza dari segi fungsi yang mereka berikan. Pertimbangkan setiap perkara dengan teliti ketika menentukan mana yang sesuai untuk pekerjaan itu.

Seperti biasa, semua contoh yang dibincangkan boleh dilayari di Github.