Pengenalan Penukar di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat java.util.concurrent.Exchanger. Ini berfungsi sebagai titik umum bagi dua utas di Java untuk menukar objek di antara mereka.

2. Pengenalan kepada Penukar

The Exchanger kelas di Jawa boleh digunakan untuk objek saham antara dua benang jenis T . Kelas hanya menyediakan satu kaedah pertukaran sahaja (T t) .

Apabila dipanggil pertukaran menunggu utas lain dalam pasangan memanggilnya juga. Pada ketika ini, utas kedua mendapati utas pertama sedang menunggu dengan objeknya. Benang menukar objek yang mereka pegang dan memberi isyarat pertukaran, dan sekarang mereka dapat kembali.

Mari lihat contoh untuk memahami pertukaran mesej antara dua utas dengan Exchanger :

@Test public void givenThreads_whenMessageExchanged_thenCorrect() { Exchanger exchanger = new Exchanger(); Runnable taskA = () -> { try { String message = exchanger.exchange("from A"); assertEquals("from B", message); } catch (InterruptedException e) { Thread.currentThread.interrupt(); throw new RuntimeException(e); } }; Runnable taskB = () -> { try { String message = exchanger.exchange("from B"); assertEquals("from A", message); } catch (InterruptedException e) { Thread.currentThread.interrupt(); throw new RuntimeException(e); } }; CompletableFuture.allOf( runAsync(taskA), runAsync(taskB)).join(); }

Di sini, kami mempunyai dua utas yang saling bertukar-tukar mesej menggunakan penukar biasa. Mari lihat contoh di mana kita menukar objek dari utas utama dengan utas baru:

@Test public void givenThread_WhenExchangedMessage_thenCorrect() throws InterruptedException { Exchanger exchanger = new Exchanger(); Runnable runner = () -> { try { String message = exchanger.exchange("from runner"); assertEquals("to runner", message); } catch (InterruptedException e) { Thread.currentThread.interrupt(); throw new RuntimeException(e); } }; CompletableFuture result = CompletableFuture.runAsync(runner); String msg = exchanger.exchange("to runner"); assertEquals("from runner", msg); result.join(); }

Perhatikan bahawa, kita perlu memulakan thread pelari terlebih dahulu dan kemudian pertukaran panggilan () di utas utama.

Juga, perhatikan bahawa panggilan utas pertama mungkin tamat jika urutan kedua tidak mencapai titik pertukaran dalam masa. Berapa lama utas pertama yang harus ditunggu dapat dikendalikan menggunakan pertukaran yang terlalu banyak (T t, timeout yang panjang, TimeUnit timeUnit).

3. Tiada Pertukaran Data GC

Exchanger dapat digunakan untuk membuat pola pipa dengan mengirimkan data dari satu utas ke benang yang lain. Di bahagian ini, kami akan membuat timbunan benang mudah terus menerus menyampaikan data antara satu sama lain sebagai saluran paip.

@Test public void givenData_whenPassedThrough_thenCorrect() throws InterruptedException { Exchanger
    
      readerExchanger = new Exchanger(); Exchanger
     
       writerExchanger = new Exchanger(); Runnable reader = () -> { Queue readerBuffer = new ConcurrentLinkedQueue(); while (true) { readerBuffer.add(UUID.randomUUID().toString()); if (readerBuffer.size() >= BUFFER_SIZE) { readerBuffer = readerExchanger.exchange(readerBuffer); } } }; Runnable processor = () -> { Queue processorBuffer = new ConcurrentLinkedQueue(); Queue writerBuffer = new ConcurrentLinkedQueue(); processorBuffer = readerExchanger.exchange(processorBuffer); while (true) { writerBuffer.add(processorBuffer.poll()); if (processorBuffer.isEmpty()) { processorBuffer = readerExchanger.exchange(processorBuffer); writerBuffer = writerExchanger.exchange(writerBuffer); } } }; Runnable writer = () -> { Queue writerBuffer = new ConcurrentLinkedQueue(); writerBuffer = writerExchanger.exchange(writerBuffer); while (true) { System.out.println(writerBuffer.poll()); if (writerBuffer.isEmpty()) { writerBuffer = writerExchanger.exchange(writerBuffer); } } }; CompletableFuture.allOf( runAsync(reader), runAsync(processor), runAsync(writer)).join(); }
     
    

Di sini, kita mempunyai tiga utas: pembaca , pemproses , dan penulis . Bersama-sama, mereka berfungsi sebagai satu saluran pertukaran data antara mereka.

The readerExchanger dikongsi antara pembaca dan pemproses benang, manakala writerExchanger dikongsi antara pemproses dan penulis thread.

Perhatikan bahawa contoh di sini hanya untuk demonstrasi. Kita mesti berhati-hati semasa membuat gelung tanpa batas dengan sementara (benar) . Untuk memastikan kod tetap dapat dibaca, kami telah menghilangkan beberapa pengendalian pengecualian.

Pola pertukaran data ini semasa menggunakan semula penyangga memungkinkan pengumpulan sampah lebih sedikit. Kaedah pertukaran mengembalikan keadaan antrian yang sama dan dengan itu tidak akan ada GC untuk objek ini. Tidak seperti barisan penyekat, penukar tidak membuat sebarang nod atau objek untuk menahan dan berkongsi data.

Membuat saluran paip seperti ini mirip dengan pola Disrupter, dengan perbezaan utama, corak Disrupter menyokong pelbagai pengeluar dan pengguna, sementara penukar dapat digunakan antara sepasang pengguna dan pengeluar.

4. Kesimpulan

Oleh itu, kami telah mengetahui apa itu Exchanger di Java, bagaimana ia berfungsi, dan kami telah melihat bagaimana menggunakan kelas Exchanger . Juga, kami membuat saluran paip dan menunjukkan pertukaran data tanpa GC antara utas.

Seperti biasa, kodnya tersedia di GitHub.