Panduan Java Phaser

1. Gambaran keseluruhan

Dalam artikel ini, kita akan melihat konstruksi Phaser dari pakej java.util.concurrent . Ini adalah konstruksi yang sangat serupa dengan CountDownLatch yang membolehkan kita menyelaraskan pelaksanaan utas. Sebagai perbandingan dengan CountDownLatch , ia mempunyai beberapa fungsi tambahan.

The Phaser menjadi penghalang di mana jumlah dinamik benang perlu menunggu sebelum meneruskan pelaksanaan. Dalam CountDownLatch nombor tersebut tidak dapat dikonfigurasi secara dinamik dan perlu diberikan semasa kita membuat instance.

2. API Phaser

The Phaser membolehkan kita untuk membina logik di mana benang perlu menunggu pada halangan sebelum pergi ke langkah seterusnya pelaksanaan .

Kami dapat mengkoordinasikan beberapa fasa pelaksanaan, menggunakan kembali contoh Phaser untuk setiap fasa program. Setiap fasa boleh mempunyai bilangan utas yang berbeza menunggu untuk maju ke fasa lain. Kita akan melihat contoh penggunaan fasa di kemudian hari.

Untuk mengambil bahagian dalam koordinasi, utas perlu mendaftar () dengan contoh Phaser . Perhatikan bahawa ini hanya meningkatkan jumlah pihak yang terdaftar, dan kami tidak dapat memeriksa apakah benang semasa didaftarkan - kami harus menundukkan pelaksanaan untuk menyokong ini.

Benang memberi isyarat bahawa ia tiba di penghalang dengan memanggil tibaAndAwaitAdvance () , yang merupakan kaedah penyekat. Apabila jumlah pihak yang tiba sama dengan jumlah pihak yang berdaftar, pelaksanaan program akan diteruskan , dan jumlah fasa akan meningkat. Kita boleh mendapatkan nombor fasa semasa dengan memanggil kaedah getPhase () .

Apabila utas menyelesaikan tugasnya, kita harus memanggil kaedah tibaAndDeregister () untuk memberi isyarat bahawa utas semasa tidak lagi dipertanggungjawabkan dalam fasa ini.

3. Melaksanakan Logik Menggunakan Phaser API

Katakan bahawa kita ingin menyelaraskan pelbagai fasa tindakan. Tiga utas akan memproses fasa pertama, dan dua utas akan memproses fasa kedua.

Kami akan membuat kelas LongRunningAction yang menerapkan antara muka Runnable :

class LongRunningAction implements Runnable { private String threadName; private Phaser ph; LongRunningAction(String threadName, Phaser ph) { this.threadName = threadName; this.ph = ph; ph.register(); } @Override public void run() { ph.arriveAndAwaitAdvance(); try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } ph.arriveAndDeregister(); } }

Apabila kelas tindakan kami dibuat, kami mendaftar ke contoh Phaser menggunakan kaedah register () . Ini akan menambah bilangan utas menggunakan Phaser tertentu .

Panggilan untuk tibaAndAwaitAdvance () akan menyebabkan utas semasa menunggu di penghalang. Seperti yang telah disebutkan, ketika jumlah pihak yang tiba menjadi sama dengan jumlah pihak yang berdaftar, pelaksanaannya akan diteruskan.

Setelah pemprosesan selesai, utas semasa membatalkan pendaftarannya sendiri dengan memanggil kaedah tibaAndDeregister () .

Mari buat kes ujian di mana kita akan memulakan tiga utas LongRunningAction dan menyekat pada penghalang. Selanjutnya, setelah tindakan selesai, kami akan membuat dua utas LongRunningAction tambahan yang akan melakukan pemprosesan pada fasa seterusnya.

Semasa membuat contoh Phaser dari utas utama, kami menyampaikan 1 sebagai argumen. Ini sama dengan memanggil kaedah register () dari utas semasa. Kami melakukan ini kerana, semasa kami membuat tiga utas pekerja, utas utamanya adalah penyelaras, dan oleh itu Phaser perlu mendaftarkan empat utas:

ExecutorService executorService = Executors.newCachedThreadPool(); Phaser ph = new Phaser(1); assertEquals(0, ph.getPhase());

Fasa selepas inisialisasi sama dengan sifar.

The Phaser kelas mempunyai pembina di mana kita boleh lulus contoh ibu bapa kepadanya. Ia berguna sekiranya kita mempunyai banyak pihak yang akan mengalami kos pertikaian penyegerakan yang besar. Dalam situasi seperti itu, contoh Phasers dapat disusun supaya kumpulan sub-phaser berkongsi ibu bapa yang sama.

Seterusnya, mari kita mulakan tiga utas tindakan LongRunningAction , yang akan menunggu di penghalang sehingga kita akan memanggil kaedah tibaAndAwaitAdvance () dari utas utama.

Perlu diingat bahawa kami telah memulakan Phaser kami dengan 1 dan memanggil register () tiga kali lagi. Sekarang, tiga utas tindakan telah mengumumkan bahawa mereka telah sampai di penghalang, jadi diperlukan satu lagi panggilan tibaAndAwaitAdvance () - satu dari utas utama:

executorService.submit(new LongRunningAction("thread-1", ph)); executorService.submit(new LongRunningAction("thread-2", ph)); executorService.submit(new LongRunningAction("thread-3", ph)); ph.arriveAndAwaitAdvance(); assertEquals(1, ph.getPhase());

Setelah menyelesaikan fasa itu, kaedah getPhase () akan mengembalikan satu kerana program selesai memproses langkah pertama pelaksanaan.

Katakan bahawa dua utas harus melakukan fasa pemprosesan seterusnya. Kami dapat memanfaatkan Phaser untuk mencapainya kerana ia membolehkan kami mengkonfigurasi bilangan utas secara dinamik yang harus menunggu pada penghalang. Kami memulakan dua utas baru, tetapi ini tidak akan dilaksanakan sehingga panggilan ke tibaAndAwaitAdvance () dari utas utama (sama seperti dalam kes sebelumnya):

executorService.submit(new LongRunningAction("thread-4", ph)); executorService.submit(new LongRunningAction("thread-5", ph)); ph.arriveAndAwaitAdvance(); assertEquals(2, ph.getPhase()); ph.arriveAndDeregister();

Selepas ini, kaedah getPhase () akan mengembalikan nombor fasa yang sama dengan dua. Apabila kita ingin menyelesaikan program kita, kita perlu memanggil kaedah tibaAndDeregister () kerana utas utama masih didaftarkan di Phaser. Apabila pembatalan pendaftaran menyebabkan bilangan pihak yang berdaftar untuk menjadi sifar, Phaser adalah ditamatkan. Semua kaedah panggilan ke penyegerakan tidak akan disekat lagi dan akan segera kembali.

Menjalankan program akan menghasilkan output berikut (kod sumber penuh dengan pernyataan garis cetak boleh didapati di repositori kod):

This is phase 0 This is phase 0 This is phase 0 Thread thread-2 before long running action Thread thread-1 before long running action Thread thread-3 before long running action This is phase 1 This is phase 1 Thread thread-4 before long running action Thread thread-5 before long running action

Kami melihat bahawa semua utas menunggu pelaksanaan sehingga penghalang terbuka. Fasa pelaksanaan seterusnya hanya dilakukan apabila yang sebelumnya selesai dengan jayanya.

4. Kesimpulan

Dalam tutorial ini, kami melihat konstruk Phaser dari java.util.concurrent dan kami melaksanakan logik koordinasi dengan pelbagai fasa menggunakan kelas Phaser .

Pelaksanaan semua contoh dan coretan kod ini terdapat dalam projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.