Cetak Nombor Genap dan Ganjil Menggunakan 2 Benang

1. Pengenalan

Dalam tutorial ini, kita akan melihat bagaimana kita dapat mencetak nombor genap dan ganjil menggunakan dua utas.

Tujuannya adalah untuk mencetak nombor mengikut urutan, sementara satu utas hanya mencetak nombor genap dan utas yang lain hanya mencetak nombor ganjil. Kami akan menggunakan konsep penyegerakan benang dan komunikasi antara utas untuk menyelesaikan masalah.

2. Benang di Jawa

Benang adalah proses ringan yang dapat dijalankan secara serentak. Pelaksanaan serentak beberapa utas dapat memberikan kesan yang baik mengenai prestasi dan penggunaan CPU kerana kita dapat mengerjakan lebih dari satu tugas sekaligus melalui rangkaian yang berbeza yang berjalan secara selari.

Maklumat lebih lanjut mengenai utas di Java boleh didapati dalam artikel ini.

Di Java, kita dapat membuat utas dengan memperluas kelas Thread atau dengan menerapkan antara muka Runnable . Dalam kedua kes tersebut, kami mengesampingkan kaedah larian dan menulis pelaksanaan utas di dalamnya.

Maklumat lebih lanjut mengenai cara menggunakan kaedah ini untuk membuat utas boleh didapati di sini.

3. Penyegerakan Thread

Dalam persekitaran berbilang utas, ada kemungkinan 2 atau lebih utas mengakses sumber yang sama pada waktu yang sama. Ini boleh membawa maut dan membawa kepada keputusan yang salah. Untuk mengelakkannya, kita perlu memastikan bahawa hanya satu utas yang mengakses sumber pada satu masa tertentu.

Kami dapat mencapainya menggunakan penyegerakan utas.

Di Java, kita dapat menandai satu kaedah atau blok sebagai disegerakkan, yang bermaksud bahawa hanya satu utas yang dapat memasukkan kaedah itu atau menyekat pada suatu waktu tertentu.

Maklumat lebih lanjut mengenai penyegerakan benang di Java boleh didapati di sini.

4. Komunikasi Antara Benang

Komunikasi antara benang membolehkan utas yang disegerakkan untuk berkomunikasi antara satu sama lain menggunakan satu set kaedah.

Kaedah yang digunakan adalah menunggu , memberitahu, dan memberitahu semua, yang semuanya diwarisi dari kelas Objek .

Tunggu () menyebabkan utas semasa menunggu selama-lamanya sehingga beberapa panggilan utas lain memberitahu () atau notifyAll () pada objek yang sama. Kita boleh memanggil notify () untuk membangunkan thread yang sedang menunggu akses ke monitor objek ini.

Maklumat lanjut mengenai kaedah ini boleh didapati di sini.

5. Mencetak Nombor Ganjil dan Genap Sebagai alternatif

5.1. Menggunakan menunggu () dan memberitahu ()

Kami akan menggunakan konsep penyegerakan dan komunikasi antara utas yang dibincangkan untuk mencetak nombor ganjil dan genap dalam urutan menaik menggunakan dua utas yang berbeza.

Pada langkah pertama, kami akan melaksanakan antara muka Runnable untuk menentukan logik kedua-dua utas . Dalam kaedah larian , kami memeriksa sama ada bilangannya genap atau ganjil.

Sekiranya nombornya genap, kami memanggil kaedah printEven dari kelas Printer , jika tidak, kami memanggil kaedah printOdd :

class TaskEvenOdd implements Runnable { private int max; private Printer print; private boolean isEvenNumber; // standard constructors @Override public void run() { int number = isEvenNumber ? 2 : 1; while (number <= max) { if (isEvenNumber) { print.printEven(number); } else { print.printOdd(number); } number += 2; } } } 

Kami menentukan kelas Pencetak seperti berikut:

class Printer { private volatile boolean isOdd; synchronized void printEven(int number) { while (!isOdd) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println(Thread.currentThread().getName() + ":" + number); isOdd = false; notify(); } synchronized void printOdd(int number) { while (isOdd) { try { wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println(Thread.currentThread().getName() + ":" + number); isOdd = true; notify(); } }

Dalam kaedah utama, kami menggunakan kelas yang ditentukan untuk membuat dua utas. Kami membuat objek kelas Pencetak dan meneruskannya sebagai parameter ke pembangun TaskEvenOdd :

public static void main(String... args) { Printer print = new Printer(); Thread t1 = new Thread(new TaskEvenOdd(print, 10, false),"Odd"); Thread t2 = new Thread(new TaskEvenOdd(print, 10, true),"Even"); t1.start(); t2.start(); }

Benang pertama akan menjadi utas ganjil, oleh itu kami lulus palsu kerana nilai parameternya adalahEvenNumber . Untuk thread kedua, kita lulus benar sebaliknya. Kami menetapkan maxValue ke 10 untuk kedua-dua utas , sehingga hanya angka dari 1 hingga 10 yang dicetak.

Kami kemudian memulakan kedua-dua utas dengan memanggil kaedah start () . Ini akan menggunakan kaedah run () kedua-dua utas seperti yang ditentukan di atas di mana kita memeriksa sama ada nombor itu ganjil atau genap dan mencetaknya.

Apabila utas ganjil mula berjalan, nilai nombor pemboleh ubah akan menjadi 1. Oleh kerana nilai ini kurang daripada nilai maks dan bendera adalahEvenNumber salah, printOdd () dipanggil Dalam kaedah tersebut, kami memeriksa sama ada bendera isOdd benar dan sementara itu benar, kami memanggil tunggu (). Oleh kerana isOdd pada mulanya salah, tunggu () tidak dipanggil, dan nilainya dicetak.

Kami kemudian menetapkan nilai isOdd ke true, supaya benang ganjil masuk ke keadaan tunggu dan panggil memberitahu () untuk membangunkan urutan genap. Benang genap kemudian bangun dan mencetak nombor genap kerana bendera ganjil itu palsu. Ia kemudian memanggil notify () untuk membangunkan utas ganjil.

Proses yang sama dilakukan sehingga nilai nombor pemboleh ubah lebih besar daripada nilai maxValue .

5.2. Menggunakan Semaphores

Semaphore mengawal akses ke sumber yang dikongsi melalui penggunaan kaunter. Sekiranya kaunter lebih besar daripada sifar, maka akses dibenarkan . Sekiranya ia sifar, maka akses ditolak.

Java menyediakan kelas Semaphore dalam paket java.util.concurrent dan kita dapat menggunakannya untuk melaksanakan mekanisme yang dijelaskan. Maklumat lebih lanjut mengenai semaphores boleh didapati di sini.

Kami membuat dua utas, utas ganjil, dan utas genap. Benang ganjil akan mencetak nombor ganjil bermula dari 1, dan utas genap akan mencetak nombor genap bermula dari 2.

Kedua-dua utas mempunyai objek kelas SharedPrinter . The SharedPrinter kelas akan mempunyai dua semaphores, semOdd dan semEven yang akan mempunyai 1 dan 0 permit untuk memulakan dengan . Ini akan memastikan nombor ganjil dicetak terlebih dahulu.

We have two methods printEvenNum() and printOddNum(). The odd thread calls the printOddNum() method and the even thread calls the printEvenNum() method.

To print an odd number, the acquire() method is called on semOdd, and since the initial permit is 1, it acquires the access successfully, prints the odd number and calls release() on semEven.

Calling release() will increment the permit by 1 for semEven, and the even thread can then successfully acquire the access and print the even number.

This is the code for the workflow described above:

public static void main(String[] args) { SharedPrinter sp = new SharedPrinter(); Thread odd = new Thread(new Odd(sp, 10),"Odd"); Thread even = new Thread(new Even(sp, 10),"Even"); odd.start(); even.start(); }
class SharedPrinter { private Semaphore semEven = new Semaphore(0); private Semaphore semOdd = new Semaphore(1); void printEvenNum(int num) { try { semEven.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(Thread.currentThread().getName() + num); semOdd.release(); } void printOddNum(int num) { try { semOdd.acquire(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println(Thread.currentThread().getName() + num); semEven.release(); } } class Even implements Runnable { private SharedPrinter sp; private int max; // standard constructor @Override public void run() { for (int i = 2; i <= max; i = i + 2) { sp.printEvenNum(i); } } } class Odd implements Runnable { private SharedPrinter sp; private int max; // standard constructors @Override public void run() { for (int i = 1; i <= max; i = i + 2) { sp.printOddNum(i); } } }

6. Conclusion

Dalam tutorial ini, kita melihat bagaimana kita dapat mencetak nombor ganjil dan genap secara alternatif menggunakan dua utas di Java. Kami melihat dua kaedah untuk mencapai hasil yang sama: menggunakan menunggu () dan memberitahu () dan menggunakan Semaphore .

Dan, seperti biasa, kod kerja penuh tersedia di GitHub.