Cetak Angka Genap dan Ganjil Menggunakan 2 Benang

1. Perkenalan

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

Tujuannya adalah untuk mencetak angka secara berurutan, sedangkan satu utas hanya mencetak angka genap dan utas lainnya hanya mencetak angka ganjil. Kami akan menggunakan konsep sinkronisasi utas dan komunikasi antar utas untuk menyelesaikan masalah.

2. Utas di Jawa

Thread adalah proses ringan yang dapat dijalankan secara bersamaan. Eksekusi bersamaan dari beberapa utas bisa jadi bagus terkait kinerja dan penggunaan CPU karena kami dapat mengerjakan lebih dari satu tugas sekaligus melalui utas berbeda yang berjalan secara paralel.

Informasi lebih lanjut tentang utas di Java dapat ditemukan di artikel ini.

Di Java, kita bisa membuat thread dengan memperluas kelas Thread atau dengan mengimplementasikan antarmuka Runnable . Dalam kedua kasus tersebut, kami mengganti metode run dan menulis implementasi thread di dalamnya.

Informasi lebih lanjut tentang cara menggunakan metode ini untuk membuat utas dapat ditemukan di sini.

3. Sinkronisasi Benang

Dalam lingkungan multi-utas, ada kemungkinan bahwa 2 utas atau lebih mengakses sumber daya yang sama di waktu yang sama. Ini bisa berakibat fatal dan menyebabkan hasil yang salah. Untuk mencegah hal ini, kita perlu memastikan bahwa hanya satu utas yang mengakses sumber daya pada titik waktu tertentu.

Kami dapat mencapai ini menggunakan sinkronisasi utas.

Di Java, kita dapat menandai metode atau blok sebagai tersinkronisasi, yang berarti bahwa hanya satu utas yang dapat memasuki metode atau blok itu pada titik waktu tertentu.

Rincian lebih lanjut tentang sinkronisasi utas di Jawa dapat ditemukan di sini.

4. Komunikasi Antar Benang

Komunikasi antar utas memungkinkan utas yang disinkronkan untuk berkomunikasi satu sama lain menggunakan serangkaian metode.

Metode yang digunakan adalah wait , notify, dan notifyAll, yang semuanya diwarisi dari kelas Object .

Wait () menyebabkan utas saat ini menunggu tanpa batas hingga beberapa utas lain memanggil notify () atau notifyAll () pada objek yang sama. Kita bisa memanggil notify () untuk membangunkan thread yang menunggu akses ke monitor objek ini.

Rincian lebih lanjut tentang cara kerja metode ini dapat ditemukan di sini.

5. Mencetak Nomor Ganjil dan Genap Sebagai Alternatif

5.1. Menggunakan wait () dan notify ()

Kami akan menggunakan konsep sinkronisasi dan komunikasi antar utas yang telah dibahas untuk mencetak angka ganjil dan genap dalam urutan naik menggunakan dua utas yang berbeda.

Pada langkah pertama, kami akan mengimplementasikan antarmuka Runnable untuk menentukan logika kedua utas . Dalam metode run , kami memeriksa apakah angkanya genap atau ganjil.

Jika angkanya genap, kami memanggil metode printEven dari kelas Printer , jika tidak, kami memanggil metode 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 mendefinisikan kelas Printer sebagai 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 metode utama, kami menggunakan kelas yang ditentukan untuk membuat dua utas. Kami membuat objek dari kelas Printer dan meneruskannya sebagai parameter ke konstruktor 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(); }

Utas pertama akan menjadi utas ganjil, oleh karena itu kami mengirimkan false sebagai nilai parameter isEvenNumber . Untuk utas kedua, kami meneruskan true . Kami menyetel maxValue menjadi 10 untuk kedua utas, sehingga hanya angka dari 1 hingga 10 yang dicetak.

Kami kemudian memulai kedua utas dengan memanggil metode start () . Ini akan memanggil metode run () dari kedua utas seperti yang didefinisikan di atas di mana kami memeriksa apakah jumlahnya ganjil atau genap dan mencetaknya.

Ketika thread ganjil mulai berjalan, nilai nomor variabel akan menjadi 1. Karena lebih kecil dari maxValue dan flag isEvenNumber salah, printOdd () dipanggil. Dalam metode ini, kami memeriksa apakah flag isOdd benar dan sementara itu benar kami memanggil wait (). Karena isOdd awalnya salah, wait () tidak dipanggil, dan nilainya dicetak.

Kami kemudian menyetel nilai isOdd ke true, sehingga utas ganjil masuk ke status tunggu dan memanggil notify () untuk membangunkan utas genap. Benang genap kemudian bangun dan mencetak nomor genap karena bendera ganjil salah. Ini kemudian memanggil notify () untuk membangunkan utas aneh.

Proses yang sama dilakukan sampai nilai nomor variabel lebih besar dari maxValue .

5.2. Menggunakan Semaphores

Semaphore mengontrol akses ke sumber daya bersama melalui penggunaan penghitung. Jika penghitung lebih besar dari nol, maka akses diperbolehkan . Jika nol, maka akses ditolak.

Java menyediakan kelas Semaphore dalam paket java.util.concurrent dan kita dapat menggunakannya untuk mengimplementasikan mekanisme yang dijelaskan. Detail lebih lanjut tentang semaphore dapat ditemukan di sini.

Kami membuat dua utas, utas ganjil, dan utas genap. Utas ganjil akan mencetak angka ganjil mulai dari 1, dan utas genap akan mencetak angka genap mulai dari 2.

Kedua utas memiliki objek kelas SharedPrinter . Kelas SharedPrinter akan memiliki dua semaphore, semOdd dan semEven yang akan memiliki 1 dan 0 izin untuk memulai . Ini akan memastikan bahwa nomor 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, kami telah melihat bagaimana kami dapat mencetak angka ganjil dan genap secara bergantian menggunakan dua utas di Java. Kami telah melihat dua metode untuk mencapai hasil yang sama: menggunakan wait () dan notify () dan menggunakan Semaphore .

Dan, seperti biasa, kode yang berfungsi lengkap tersedia di GitHub.