CyclicBarrier di Jawa

1. Pengenalan

CyclicBarriers adalah konstruk penyegerakan yang diperkenalkan dengan Java 5 sebagai bahagian dari pakej java.util.concurrent .

Dalam artikel ini, kita akan menerangkan pelaksanaan ini dalam senario serentak.

2. Java Concurrency - Penyegerak

The java.util.concurrent pakej mengandungi beberapa kelas yang membantu menguruskan satu set benang yang bekerjasama antara satu sama lain. Beberapa di antaranya termasuk:

  • CyclicBarrier
  • Phaser
  • CountDownLatch
  • Penukar
  • Semaphore
  • Segerak Segera

Kelas-kelas ini menawarkan fungsi luar kotak untuk corak interaksi umum antara utas.

Sekiranya kita mempunyai sekumpulan utas yang saling berkomunikasi dan menyerupai salah satu corak umum, kita hanya dapat menggunakan semula kelas perpustakaan yang sesuai (juga disebut Penyegerak ) daripada berusaha membuat skema khusus menggunakan satu set kunci dan keadaan objek dan kata kunci yang disegerakkan .

Mari fokus pada CyclicBarrier ke hadapan.

3. CyclicBarrier

A CyclicBarrier ialah Synchronizer yang membolehkan satu set benang menunggu satu sama lain untuk mencapai tahap pelaksanaan yang sama, juga dikenali sebagai halangan .

CyclicBarriers digunakan dalam program di mana kita mempunyai bilangan utas yang tetap yang mesti saling menunggu untuk mencapai titik yang sama sebelum meneruskan pelaksanaan.

Penghalang disebut siklik kerana boleh digunakan semula setelah benang menunggu dilepaskan.

4. Penggunaan

Pembina untuk CyclicBarrier adalah mudah. Dibutuhkan bilangan bulat tunggal yang menunjukkan bilangan utas yang perlu memanggil kaedah tunggu () pada contoh penghalang untuk menandakan mencapai titik pelaksanaan biasa:

public CyclicBarrier(int parties)

Benang yang perlu diselaraskan pelaksanaannya juga disebut pihak dan memanggil kaedah tunggu () adalah bagaimana kita dapat mendaftarkan bahawa utas tertentu telah mencapai titik penghalang.

Panggilan ini segerak dan utas memanggil kaedah ini menangguhkan pelaksanaan sehingga sejumlah utas tertentu memanggil kaedah yang sama pada penghalang. Keadaan ini di mana bilangan utas yang diperlukan telah dipanggil menunggu () , disebut sebagai penghalang .

Secara pilihan, kita dapat menyampaikan argumen kedua kepada konstruktor, yang merupakan contoh Runnable . Ini mempunyai logik yang akan dijalankan oleh utas terakhir yang melepasi halangan:

public CyclicBarrier(int parties, Runnable barrierAction)

5. Pelaksanaan

Untuk melihat CyclicBarrier beraksi, mari kita pertimbangkan senario berikut:

Terdapat operasi yang dilakukan oleh sejumlah utas dan menyimpan hasil yang sesuai dalam senarai. Apabila semua utas selesai melakukan aksi mereka, salah satunya (biasanya yang terakhir melintasi penghalang) mula memproses data yang diambil oleh masing-masing.

Mari kita laksanakan kelas utama di mana semua tindakan berlaku:

public class CyclicBarrierDemo { private CyclicBarrier cyclicBarrier; private List
    
      partialResults = Collections.synchronizedList(new ArrayList()); private Random random = new Random(); private int NUM_PARTIAL_RESULTS; private int NUM_WORKERS; // ... }
    

Kelas ini agak lurus ke hadapan - NUM_WORKERS adalah bilangan utas yang akan dilaksanakan dan NUM_PARTIAL_RESULTS adalah jumlah hasil yang akan dihasilkan oleh setiap utas pekerja.

Akhirnya, kami mempunyai keputusan sebahagian yang merupakan senarai yang akan menyimpan hasil setiap utas pekerja ini. Perhatikan bahawa senarai ini adalah SynchronizedList kerana banyak utas akan menulisnya pada masa yang sama, dan kaedah add () tidak selamat pada thread pada ArrayList biasa .

Sekarang mari kita laksanakan logik setiap utas pekerja:

public class CyclicBarrierDemo { // ... class NumberCruncherThread implements Runnable { @Override public void run() { String thisThreadName = Thread.currentThread().getName(); List partialResult = new ArrayList(); // Crunch some numbers and store the partial result for (int i = 0; i < NUM_PARTIAL_RESULTS; i++) { Integer num = random.nextInt(10); System.out.println(thisThreadName + ": Crunching some numbers! Final result - " + num); partialResult.add(num); } partialResults.add(partialResult); try { System.out.println(thisThreadName + " waiting for others to reach barrier."); cyclicBarrier.await(); } catch (InterruptedException e) { // ... } catch (BrokenBarrierException e) { // ... } } } }

Kami sekarang akan menerapkan logik yang berjalan ketika penghalang telah diatasi.

Untuk mempermudah, mari kita tambahkan semua nombor dalam senarai keputusan separa:

public class CyclicBarrierDemo { // ... class AggregatorThread implements Runnable { @Override public void run() { String thisThreadName = Thread.currentThread().getName(); System.out.println( thisThreadName + ": Computing sum of " + NUM_WORKERS + " workers, having " + NUM_PARTIAL_RESULTS + " results each."); int sum = 0; for (List threadResult : partialResults) { System.out.print("Adding "); for (Integer partialResult : threadResult) { System.out.print(partialResult+" "); sum += partialResult; } System.out.println(); } System.out.println(thisThreadName + ": Final result = " + sum); } } }

Langkah terakhir adalah membina CyclicBarrier dan memulakan sesuatu dengan kaedah utama () :

public class CyclicBarrierDemo { // Previous code public void runSimulation(int numWorkers, int numberOfPartialResults) { NUM_PARTIAL_RESULTS = numberOfPartialResults; NUM_WORKERS = numWorkers; cyclicBarrier = new CyclicBarrier(NUM_WORKERS, new AggregatorThread()); System.out.println("Spawning " + NUM_WORKERS + " worker threads to compute " + NUM_PARTIAL_RESULTS + " partial results each"); for (int i = 0; i < NUM_WORKERS; i++) { Thread worker = new Thread(new NumberCruncherThread()); worker.setName("Thread " + i); worker.start(); } } public static void main(String[] args) { CyclicBarrierDemo demo = new CyclicBarrierDemo(); demo.runSimulation(5, 3); } } 

Dalam kod di atas, kami menginisialisasi penghalang siklik dengan 5 utas yang masing-masing menghasilkan 3 bilangan bulat sebagai sebahagian daripada pengiraannya dan menyimpannya sama dalam senarai yang dihasilkan.

Setelah penghalang tersandung, utas terakhir yang melintasi penghalang melaksanakan logik yang ditentukan dalam AggregatorThread, iaitu - tambahkan semua nombor yang dihasilkan oleh utas.

6. Hasil

Berikut adalah hasil dari satu pelaksanaan program di atas - setiap pelaksanaan mungkin menghasilkan hasil yang berbeza kerana benang dapat dihasilkan dalam urutan yang berbeza:

Spawning 5 worker threads to compute 3 partial results each Thread 0: Crunching some numbers! Final result - 6 Thread 0: Crunching some numbers! Final result - 2 Thread 0: Crunching some numbers! Final result - 2 Thread 0 waiting for others to reach barrier. Thread 1: Crunching some numbers! Final result - 2 Thread 1: Crunching some numbers! Final result - 0 Thread 1: Crunching some numbers! Final result - 5 Thread 1 waiting for others to reach barrier. Thread 3: Crunching some numbers! Final result - 6 Thread 3: Crunching some numbers! Final result - 4 Thread 3: Crunching some numbers! Final result - 0 Thread 3 waiting for others to reach barrier. Thread 2: Crunching some numbers! Final result - 1 Thread 2: Crunching some numbers! Final result - 1 Thread 2: Crunching some numbers! Final result - 0 Thread 2 waiting for others to reach barrier. Thread 4: Crunching some numbers! Final result - 9 Thread 4: Crunching some numbers! Final result - 3 Thread 4: Crunching some numbers! Final result - 5 Thread 4 waiting for others to reach barrier. Thread 4: Computing final sum of 5 workers, having 3 results each. Adding 6 2 2 Adding 2 0 5 Adding 6 4 0 Adding 1 1 0 Adding 9 3 5 Thread 4: Final result = 46 

Seperti yang ditunjukkan oleh output di atas, Thread 4 adalah yang mengatasi halangan dan juga melaksanakan logik agregasi terakhir. Juga tidak perlu benang dijalankan mengikut urutan yang dimulakan seperti ditunjukkan di atas.

7. Kesimpulannya

Dalam artikel ini, kami melihat apa itu CyclicBarrier , dan situasi seperti apa yang berguna.

Kami juga melaksanakan senario di mana kami memerlukan sejumlah utas untuk mencapai titik pelaksanaan tetap, sebelum meneruskan logik program lain.

Seperti biasa, kod untuk tutorial boleh didapati di GitHub.