Object
, yaitu wait()
dan notify()
.wait() dan notify()
Pertama-tama penting untuk mengerti bahwa
sleep()
tidak melepas kunci thread ketika dipanggil. Artinya jika sleep()
dipanggil dari dalam bagian kritis, maka thread lain tidak bisa masuk hingga thread yang memanggil sleep()
bangun, meneruskan eksekusi, hingga keluar dari bagian kritis. Sedangkan wait()
melepas kunci ketika dipanggil, sehingga thread lain bisa masuk ke dalam bagian kritis.Ada dua bentuk
wait()
. Yang pertama memiliki argumen waktu dalam bentuk mili detik (mirip dengan sleep()
. Perbedaannya dengan sleep()
adalah :wait()
melepaskan kunci- Kita bisa membatalkan
wait()
dengan menggunakannotify()
ataunotifyAll()
, atau hingga waktu tunggu berlalu.
wait()
adalah wait()
yang tidak memiliki argumen. Jenis wait()
ini akan terus berlangsung hingga dibatalkan dengan notify
atau notifyAll()
.Aspek penting dari
wait()
, notify()
dan notifyAll()
adalah metode ini merupakan bagian dari kelas dasar Obejct
dan bukan bagian dari kelas Thread
seperti sleep()
. Meskipun kelihatan janggal, hal ini sangat penting karena semua objek memiliki kunci. Artinya kita bisa memanggil wait()
dari dalam metode synchronized
, tidak peduli apakah kelas tersebut merupakan kelas turunan dari Thread
atau bukan.Sebetulnya satu-satunya tempat kita bisa memanggil
wait()
, notify()
dan notifyAll()
adalah dari dalam blok atau metode synchronized
. (sleep()
bisa dipanggil dari manapun karena ia tidak berhubungan dengan kunci suatu objek). Jika kita memanggil wait()
, notify()
atau notifyAll()
dari luar metode atau blok synchronized
, compiler tidak akan memperingatkan Anda, akan tetapi ketika program dijalankan, kita akan mendapatkan pengecualian IllegalMonitorStateException
dengan pesan kesalahan yang tidak dimengerti, seprti "thread ini bukan
pemiliknya". Pesan ini berarti bahwa thread yang memanggil wait()
, notify()
atau notifyAll()
harus memiliki kunci objek sebelum bisa memanggil salah satu metode ini.Kita juga bisa meminta suatu objek untuk memanipulasi kuncinya sendiri. Caranya, pertama-tama kita harus mengambil kuncinya. Misalnya, jika kita ingin memanggil
notify()
ke suatu objek x
, kita harus melakukannya di dalam blok synchronized
untuk mengambil kunci x
, seperti :synchronized(x) { x.notify(); }
wait()
digunakan jika kita menunggu sesuatu yang dikontrol oleh sesuatu di
luar kontrol metode kita (di mana sesuatu ini hanya bisa diubah oleh
thread lain). Kita tidak ingin menunggu dan berulang-ulang menguji
apakah sesuatu itu sudah tersedia, karena cara ini akan memboroskan
penggunaan CPU. Kita bisa menggunakan wait()
untuk memerintahkan suatu thread untuk menunggu hingga sesuatu tersebut berubah, dan hanya ketika notify()
dipanggil, maka thread tersebut akan bangun dan mengeceknya. Dengan kata lain wait()
digunakan melakukan aktifitas tak-sinkron antara beberapa thread.Sebagai contoh, anggap suatu restoran memiliki satu orang koki dan satu orang pelayan. Pelayan harus menunggu hingga si koki selesai memasak makanan. Ketika koki selesai, ia akan memberi tahu pelayan, kemudian membawa makanan ini ke customer, kemudian menunggu kembali. Koki di sini kita sebut sebagai produsen, dan pelayan disebut sebagai konsumen.
package com.lyracc.rumahmakan; class Pesanan { private int i = 0; public Pesanan(int i) { this.i = i; } public String toString() { return "pesanan " + i; } } // akhir kelas Pesanan class Pelayan extends Thread { private RumahMakan rumahMakan; public Pelayan(RumahMakan r) { rumahMakan = r; start(); } public void run() { while (true) { while (rumahMakan.pesanan == null) // tunggu hingga dipanggil dengan notify oleh Koki synchronized (this) { try { wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } System.out.println("Pelayan mengantarkan " + rumahMakan.pesanan); // pesanan sudah diantar, pesanan sekarang kosong rumahMakan.pesanan = null; } } } // akhir kelas Pelayan class Koki extends Thread { private RumahMakan rumahMakan; private Pelayan pelayan; public Koki(RumahMakan r, Pelayan p) { rumahMakan = r; pelayan = p; start(); } public void run() { // masak 10 makanan for (int i = 0; i < 10; i++) { if (rumahMakan.pesanan == null) { rumahMakan.pesanan = new Pesanan(i); System.out.print("Pesanan selesai! "); // coba panggil pelayan jika tidak sibuk synchronized (pelayan) { pelayan.notify(); } } try { sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } System.out.println("Makanan habis.."); System.exit(0); } } // akhir kelas Koki public class RumahMakan { Pesanan pesanan; public static void main(String[] args) { RumahMakan rumahMakan = new RumahMakan(); Pelayan pelayan = new Pelayan(rumahMakan); new Koki(rumahMakan, pelayan); } }
<img src="/sites/java.lyracc.com/files/kerjasamathread_gbr1.png" alt="" />
Pesanan
adalah kelas sederhana yang berisi pesanan. Konstruktor menerima angka
yang diibaratkan seperti pesanan, kemudian membebanlebihkan metode toString()
untuk mencetak objek ini langsung dengan System.out.println()
.Seorang
Pelayan
harus tahu RumahMakan
tempat ia bekerja, karena ia harus ke sana untuk mengantarkan pesanan dari "jendela pemesanan", yaitu rumahMakan.pesanan
. Pada metode run()
, Pelayan
masuk dalam mode menunggu. Kuncinya dimiliki oleh pelayan ini sendiri. Kunci ini yang akan digunakan oleh Koki
untuk membangunkan Pelayan
jika makanan sudah siap dengan metode notify()
.Pada aplikasi yang lebih kompleks, misalnya jika pelayannya banyak, kita bisa memanggil
notifyAll()
untuk membangunkan semua pelayan. Setiap pelayan nanti akan menguji apakah panggilan itu untuknya atau tidak.Perhatikan bahwa
wait()
ditulis di dalam pernyataan while
untuk menguji apakah pesanan sudah datang. Mungkin ini agak terasa
ganjil karena ketika thread ini dibangunkan ketika menunggu pesanan,
seharusnya pesanannya sudah tersedia khan? Masalahnya jika aplikasinya
terdiri dari banyak pelayan, thread lain mungkin sudah keburu
mengantarkan pesanannya ketika thread ini sedang bangun. Untuk itu,
lebih aman apabila kita menggunakan bentuk berikut untuk semua aplikasi
yang menggunakan wait()
:while (sesuatuYangDitunggu) wait();
Objek
Koki
harus tahu di rumah makan mana ia bekerja. Pesanan yang dia masak akan dia letakkan pada jendela pesanan (dalam hal ini rumahMakan.pesanan
) dan dia juga harus tahu siapa Pelayan
yang akan mengantarkan pesanan yang sudah selesai dimasak.Pada contoh sederhana di atas,
Koki
membuat objek Pesanan
, kemudian setelah selesai akan memanggil Pelayan
dengan notify()
. Karena panggilan notify()
dilakukan di dalam klausa synchronized
, maka sudah bisa dipastikan Koki
memanggil pelayan jika pelayan tersebut sedang tidak digunakan oleh thread lain.Sumber : http://java.lyracc.com/belajar/java-untuk-pemula/kerjasama-antar-thread
Tidak ada komentar:
Posting Komentar