Masalah Readers/Writers dan Dining Philosophers

4.5. Masalah Readers/Writers dan Dining Philosophers

4.5.1. Gambaran Umum Masalah Readers/Writers

Masalah Readers/Writersmerupakan salah satu masalah sinkronisasi klasik yang sering digunakan untuk mendiskusikan dan membandingkan berbagai cara untuk menyelesaikan masalah sinkronisasi. Secara singkat, masalah ini terjadi ketika ada beberapa pembaca dan penulis ingin mengakses suatu berkas pada saat bersamaan.Kembali kepada masalah Readers/Writers, bahwa inti dari permasalahan ini adalah adanya beberapa pembaca dan penulis yang ingin mengakses suatu berkas secara simultan. Sebagai syarat bahwa data yang terkandung dalam berkas tersebut tetap konsisten, maka setiap kali berkas tersebut ditulis, maka hanya ada boleh maksimal satu penulis yang menulisnya. Untuk pembaca, hal ini tidak perlu dikhawatirkan sebab membaca suatu berkas tidak mengubah isi berkas. Dengan kata lain, pada suatu saat diperbolehkan untuk beberapa pembaca untuk membaca berkas tersebut. Akan tetapi, ketika ada yang sedang menulis, tidak boleh ada satupun yang membaca. Ini berarti bahwa thread penulis menjalankan tugasnya secara mutual eksklusif .

Untuk mengatasi masalah ini, ada tiga macam solusi yang akan dibahas. Dasar pembagian solusi ini adalah prioritas. Pertama, solusi dengan pembaca diprioritaskan akan dibahas. Kemudian dilanjutkan dengan solusi dengan penulis yang diprioritaskan. Terakhir, solusi dengan pembaca dan penulis saling bergantian akan dibahas. Pada setiap solusi akan dilihat mengenai tingkat kesuksesan solusi tersebut bila kita lihat dari sudut pandang syarat penyelesaian critical section. Implementasi dari setiap solusi yang diberikan di bawah ini adalah dengan menggunakan semafor.

4.5.2. Solusi Dengan Pembaca Diprioritaskan

Pada solusi ini, setiap kali ada thread pembaca yang berusaha \untuk memulai membaca ketika ada setidak-tidaknya satu thread pembaca yang sedang membaca, maka thread pembaca yang baru ingin untuk membaca akan diberikan akses untuk membaca. Tujuan dari pemberian prioritas ini adalah untuk memaksimalkan throughput. Sebagai ganti dari pemaksimalan throughput ini, thread penulis dapat berada pada antrian untuk waktu yang tak terbatas atau dapat disebut sebagaistarvation, terutama ketika terjadi permintaan untuk membaca berkas secara berkelanjutan. Padahal, agar sebuah thread penulis dapat masuk ke critical section nya, tidak boleh ada satupun thread pembaca yang sedang mengakses berkas.

Dengan demikian, solusi ini gagal memenuhi persyaratan bahwa setiap thread seharusnya tidak dibiarkan menunggu dalam waktu yang tidak terbatas karena akan mengakibatkan starvation . Akan tetapi, jika yang ingin dituju adalah throughput yang sebesar-besarnya, maka solusi ini adalah yang paling tepat.

Berikut adalah contoh implementasi dari solusi ini:

Gambar 4-12. Ilustrasi Penulis/Pembaca

/** * Tempat segala sesuatunya dimulai. * Pada kelas ini, semua thread penulis dan pembaca * dibuat dan dijalankan. Agar kemungkinannya lebih * tinggi untuk semua thread penulis dan pembaca * memiliki kesempatan bersaing secara adil mendapatkan * waktu CPU, maka prioritas thread Pembuat dibuat * maksimum, sementara semua thread penulis dan pembaca * dibuat normal. Catatan: hal ini tidak berguna dalam * lingkungan Linux. */

public class PembuatBacaTulis1 {

public static void main(String args[]) {

Thread.currentThread().setPriority(Thread.MAX_PRIORITY); Penulis penulis[] = new Penulis[N_PENULIS]; Pembaca pembaca[] = new Pembaca[N_PEMBACA]; Berkas berkas = new Berkas(); for (int i = 0; i < N_PENULIS; i++) {

penulis[i] = new Penulis(i, berkas); penulis[i].setPriority(Thread.NORM_PRIORITY); penulis[i].start();

} for (int i = 0; i < N_PEMBACA; i++) {

pembaca[i] = new Pembaca(i, berkas); pembaca[i].setPriority(Thread.NORM_PRIORITY); pembaca[i].start(); pembaca[i] = new Pembaca(i, berkas); pembaca[i].setPriority(Thread.NORM_PRIORITY); pembaca[i].start();

* kelas Penulis dengan siklus hidup sebesar batas yang diberikan oleh konstanta BATAS pada kelas PembacaBuatTulis1. Siklus hidup dari sebuah penulis adalah tidur, mulai menulis, sedang menulis dan selesai menulis.

*/ class Penulis extends Thread {

public Penulis(int id, Berkas berkas) {

this.id = id; this.berkas = berkas; hitungan = 0; setName("Penulis " + id);

} public void run()

{ while (hitungan < PembuatBacaTulis1.BATAS) {

berkas.tidur(); System.out.println("Penulis " + id + " tidur."); berkas.mulaiMenulis(); System.out.println("Penulis " + id + " mulai menulis."); berkas.tidur(); System.out.println("Penulis " + id + " sedang menulis."); berkas.selesaiMenulis(); System.out.println("Penulis " + id + " selesai menulis."); hitungan++;

private Berkas berkas; private int id; private int hitungan;

* kelas Pembaca dengan siklus hidup sebesar batas yang diberikan oleh konstanta BATAS pada kelas PembacaBuatTulis1. Siklus hidup dari sebuah pembaca adalah tidur, mulai membaca, sedang membaca dan selesai membaca.

*/ class Pembaca extends Thread {

{ this.id = id; { this.id = id;

} public void run()

{ while (hitungan < PembuatBacaTulis1.BATAS) {

berkas.tidur(); System.out.println("Pembaca " + id + " tidur."); berkas.mulaiMembaca(); System.out.println("Pembaca " + id + " mulai membaca."); berkas.tidur(); System.out.println("Pembaca " + id + " sedang membaca."); berkas.selesaiMembaca(); System.out.println("Pembaca " + id + " selesai membaca."); hitungan++;

private Berkas berkas; private int id; private int hitungan;

* Kelas ini menggambarkan apa saja yang dapat * dilakukan oleh setiap thread pembaca dan penulis * ke suatu berkas. */

class Berkas {

public Berkas() {

nPembaca = 0; brks = new Semafor(1); mutex = new Semafor(1);

} public void mulaiMembaca()

{ mutex.tunggu(); nPembaca++; if (nPembaca == 1) brks.tunggu(); mutex.sinyal();

} public void selesaiMembaca()

{ mutex.tunggu(); --nPembaca; if (nPembaca == 0) brks.sinyal(); mutex.sinyal();

} public void mulaiMenulis()

{ brks.tunggu(); }

public void selesaiMenulis() {

brks.sinyal(); }

public void tidur() {

try {

long t = (long) (Math.random() * 1000); Thread.sleep(t);

} catch (InterruptedException e) {}

} private Semafor brks, mutex;

private int nPembaca; }

/** * kelas ini merupakan implementasi semafor. * Implementasi ini merupakan modifikasi dari * implementasi semafor oleh Silberschatz, Galvin, * dan Gagne. */

final class Semafor {

public Semafor() {

nilai = 0; }

public Semafor(int n) {

nilai = n; }

public synchronized void tunggu() {

nilai--; if (nilai < 0) {

try { wait(); } catch(InterruptedException e) {}

public synchronized void sinyal() {

nilai++; if (nilai <= 0) notify(); nilai++; if (nilai <= 0) notify();

Bila contoh diatas dijalankan, maka hasil yang cukup mungkin didapatkan adalah seperti berikut:

Gambar 4-13. Tampilan Layar

Pembaca 0 tidur. Pembaca 0 mulai membaca. Pembaca 2 tidur. Pembaca 2 mulai membaca. Penulis 1 tidur. Pembaca 1 tidur. Pembaca 1 mulai membaca. Penulis 0 tidur. Penulis 2 tidur. Pembaca 1 sedang membaca. Pembaca 1 selesai membaca. Pembaca 0 sedang membaca. Pembaca 0 selesai membaca. Pembaca 2 sedang membaca. Pembaca 2 selesai membaca. Penulis 1 mulai menulis. Penulis 1 sedang menulis. Penulis 1 selesai menulis. Penulis 0 mulai menulis. Penulis 0 sedang menulis. Penulis 0 selesai menulis. Penulis 2 mulai menulis. Penulis 2 sedang menulis. Penulis 2 selesai menulis.

Seperti terlihat di atas, thread-thread pembaca (BC0, BC1, BC2) mendominasi thread-thread penulis (TL0, TL1, TL2) dalam hal akses berkas, sehingga semua thread penulis harus menunggu hingga semua thread pembaca selesai.

4.5.3. Solusi Dengan Penulis Diprioritaskan

Pada bagian ini, begitu tidak ada yang mengakses berkas dan ada thread penulis yang sedang menunggu untuk mendapatkan akses berkas tersebut, maka thread penulis ini akan langsung mendapatkan akses ke berkas. Sebaliknya, bila ada thread pembaca yang juga sama-sama menunggu, maka thread pembaca ini harus menunggu hingga semua threadpenulis yang sedang menunggu diantrian mendapatkan akses ke berkas dan menyelesaikan proses penulisannya. Walaupun pada saat tersebut ada sebuah thread pembaca yang sedang mengakses berkasnya, jika sudah ada thread penulis yang menunggu di antrian, maka semua thread pembaca yang menginginkan akses ke berkas tidak mendapatkannya hingga thread penulis yang berada diantrian mendapatkan akses yang dibutuhkannya.

Akibat dari cara prioritas seperti ini adalah adanya jaminan bahwa setiap thread pembaca akan mendapatkan informasi terbaru. Akan tetapi, hal ini berakibat bahwa jika ada sekelompok besar thread penulis yang datang pada suatu waktu, maka thread pembaca tidak akan mendapatkan giliran sampai waktu yang tidak terbatas dan terjadilah starvation pada threadpembaca. Oleh karena itu, solusi ini pun gagal memenuhi persyaratan bahwa seharusnya setiap thread tidak boleh dibiarkan menunggu dalam waktu yang tidak terbatas.

Implementasi dari solusi ini tidak jauh berbeda dengan solusi sebelumnya. Bahkan, kita hanya perlu sedikit memodifikasi kelas Berkas dengan menambahkan sebuah flag untuk menyatakan apakah ada thread penulis yang sedang menunggu di antrian. Berikut ini adalah implementasi kelas Berkas untuk solusi ini:

Gambar 4-14. Ilustrasi Penulis/Pembaca

/** * Kelas ini menggambarkan apa saja yang dapat * dilakukan oleh setiap thread pembaca dan penulis * ke suatu berkas. Kelas ini diadaptasi dari Stallings98. */

class Berkas {

public Berkas() {

nPembaca = nPenulis = 0; baca = new Semafor(1); mutex1 = new Semafor(1); mutex2 = new Semafor(1); mutex3 = new Semafor(1); tulis = new Semafor(1);

} public void mulaiMembaca()

{ mutex1.tunggu(); baca.tunggu(); mutex2.tunggu(); nPembaca++; if (nPembaca == 1) tulis.tunggu(); mutex2.sinyal(); baca.sinyal(); mutex1.sinyal();

} public void selesaiMembaca()

{ mutex2.tunggu(); nPembaca--; if (nPembaca == 0) tulis.sinyal(); mutex2.sinyal();

} public void mulaiMenulis()

{ mutex3.tunggu(); nPenulis++; if (nPenulis == 1) baca.tunggu(); mutex3.sinyal(); tulis.tunggu();

} public void selesaiMenulis()

{ tulis.sinyal(); mutex3.tunggu(); nPenulis--; { tulis.sinyal(); mutex3.tunggu(); nPenulis--;

} public void tidur()

{ try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e) {}

} private Semafor baca, tulis, mutex1, mutex2, mutex3;

private int nPembaca, nPenulis; }

Perlu diperhatikan bahwa jumlah critical section bertambah banyak seiring dengan keperluan untuk menghitung jumlah penulis yang adadi antrian. Karena jumlah criticalsection bertambah, banyak semafor yang diperlukan juga meningkat. Yang perlu diperhatikan adalah bahwa pada method mulaiMembaca(), kita memerlukan sebuah semafor tambahan untuk menjaga agar semafor baca tidak langsung begitu saja diakses. Bila semafor baca dapat langsung diakses,maka dapat terjadi deadlock.

Dengan kelas yang lain tidak berubah, maka hasil yang cukup mungkin didapatkan adalah seperti berikut:

Gambar 4-15. Tampilan Layar

Penulis 0 tidur. Penulis 0 mulai menulis. Pembaca 2 tidur. Pembaca 0 tidur. Penulis 2 tidur. Penulis 1 tidur. Penulis 0 sedang menulis. Penulis 0 selesai menulis. Penulis 2 mulai menulis. Penulis 2 sedang menulis. Penulis 2 selesai menulis. Penulis 1 mulai menulis. Penulis 1 sedang menulis. Penulis 1 selesai menulis. Pembaca 2 mulai membaca. Pembaca 0 mulai membaca. Pembaca 1 tidur. Pembaca 1 mulai membaca. Pembaca 1 sedang membaca. Pembaca 1 selesai membaca. Pembaca 2 sedang membaca. Pembaca 2 selesai membaca. Pembaca 0 sedang membaca. Pembaca 0 selesai membaca.

Berikut adalah ilustrasi dari hasil contoh di atas: Ilustrasi di atas menegaskan bahwa ketika thread penulis menunggu di antrian, maka tidak ada satupun thread

pembaca yang dapat mengakses berkas yang diinginkan hingga setiap thread penulis yang ada di antrian telah menyelesaikan tugasnya. Bila thread penulis terus berdatangan, maka thread pembaca tidak akan mendapatkan giliran sedikitpun dan dengan demikian terjadilah kelaparan.

4.5.4. Solusi Dengan Pembaca Dan Penulis Mendapat Prioritas Secara Bergantian

Karena kedua solusi sebelumnya menghasilkan kedua ekstrem dalam akses berkas (thread pembaca atau thread penulis menunggu terus di antrian tanpa ada kepastian akan dijalankan) atau dapat juga dibilang pada kedua solusi diatas tersebut selalu harus ada pihak yang starvation maka pada solusi ini diusahakan sebuah metode yang mengakomodasi kedua belah pihak. Cara untuk melakukan ini cukup mudah yaitu dengan menerapkan metode gilir-menggilir. Sebuah penulis dapat mengakses berkas jika tidak ada thread pembaca yang sedang mengakses. Setelah selesai,thread ini memberikan giliran ke thread pembaca.Sebuah pembaca dapat mengakses berkas yang ada jika tidak ada penulis yang menunggu atau bila pada saat itu adalah giliran pembaca. Pembaca pertama yang pertama selesai mengakses berkas kemudian mengganti giliran sekarang menjadi giliran penulis.

Dengan metode giliran ini, tidak ada thread yang tertunda tanpa batas. Karena setiap syarat dari penyelesaian criticalsection yang baik dipenuhi, maka cara seperti ini merupakan cara yang baik untuk menyelesaikan masalah konkurensi.

4.5.5. Masalah Dining Philosophers

Masalah ini pertama ini pertama kali ditulis dan diselesaikan oleh Djikstra pada tahun 1965.Masalah ini memodelkan masalah enkapsulasi dari ketergantungan mesin dan masalah portabilitas. Dalam masalah Dining Philosophers , diketahui sejumlah (N) filusuf yang hanya memiliki tiga status, berpikir, lapar, dan makan. Semua filusuf berada di sebuah meja makan bundar yang ditata sehingga di depan setiap filusuf ada sebuah piring berisi mie dan di antara dua piring yang bersebelahan terdapat sebuah sumpit.

Pada awalnya, semua filusuf akan berpikir selama waktu yang tidak tentu. Setelah berpikir lama, filusuf akan merasa lapar. Pada saat lapar, ia berusaha untuk mengambil 2 buah sumpit yang ada di kanan dan di kirinya untuk makan. Dia mengambil sumpitnya satu per satu. Begitu ia mendapat sebuah sumpit, ia tidak akan melepaskannya. Jika ia hanya berhasil mengambil kurang dari 2 sumpit, maka ia akan menunggu sampai 2 sumpit diambil. Begitu dia mendapatkan 2 sumpit, maka dia akan makan mienya untuk sementara waktu dan kemudian meletakkan kedua sumpitnya. Kedua sumpit ini kemudian dapat digunakan oleh filusuf-filusuf yang lain.Posisi meja Filsuf dengan menggunakan semafor.Setiap sumpit mewakili sebuah semafor. Kemudian,ketika seorang filsuf lapar,maka dia akan mencoba mengambil sumpit di kiri dan dikananya atau dengan kata lain dia akan menunggu sampai kedua sumpit itu dapat digunakan. Setelah selesai makan,sumpit diletakan kembali dan sinyal diberikan ke semafor sehingga filusuf lain yang membutuhkan dapat menggunakan sumpitnya.Dan dia sendiri kemudian kembali berpikir. Tujuan dari masalah ini adalah untuk mencari cara sehingga para filusuf tidak akan pernah mati kelaparan. Hal ini juga merupakan salah satu representasi dari pengalokasian source komputer yang terbatas dengan beberapa proses sehingga dapat mengakibatkan deadlock dan starvation.

Salah satu solusi yang mungkin langsung terlihat adalah dengan menggunakan semafor. Setiap sumpit mewakili sebuah semafor. Kemudian, ketika seorang filusuf lapar, maka dia akan mencoba mengambil sumpit di kiri dan di kanannya, atau dengan kata lain dia akan menunggu sampai kedua sumpit tersebut dapat ia gunakan. Setelah selesai makan, sumpit diletakkan kembali dan sinyal diberikan ke semafor sehingga filusuf lain yang membutuhkan dapat menggunakan sumpitnya. Berikut contoh programnya:

Gambar 4-16. Program Dining Philosopher

/** * @author Sylvia Susanto * @author V.A.Pragantha * * Program ini berlisensi GPL * * Kelas ini membuat sebuah simulasi untuk masalah

* Dining Philosophers. */

public class Dining {

public static void main (String args[]) {

Semafor sumpit[] = { new Semafor(1), new Semafor(1), new Semafor(1), new Semafor(1) };

Filsuf filsuf[] = {

new Filsuf("Filsuf A", sumpit[0], sumpit[1]), new Filsuf("Filsuf B", sumpit[1], sumpit[2]), new Filsuf("Filsuf C", sumpit[2], sumpit[3]), new Filsuf("Filsuf D", sumpit[3], sumpit[0])

}; int i=0;

while(i< 4) {

filsuf[i].start(); i++;

* Kelas ini menggambarkan kegiatan dari setiap filsuf. */

class Filsuf extends Thread {

public Filsuf(String name, Semafor sumpit1, Semafor sumpit2) {

nama = name; setName("Filsuf " + i); kiri = sumpit1; kanan = sumpit2;

} /* * Method ini dipanggil pada saat Filsuf di start */

public void run() { int i=0; try {

while (true) {

System.out.println(this + " sedang berpikir"); berpikir(); System.out.println(this + " lapar"); makan(); System.out.println(this + " sudah kenyang");

} catch(Exception e) {

System.out.println(e); System.exit(0);

} public void berpikir()

{ try { Thread.sleep((long) (Math.random() * 1000)); } catch (InterruptedException e){} }

public void makan() {

kanan.tunggu(); System.out.println(" "+nama+" melihat sumpit kanan..ternyata ada.."); kiri.tunggu(); System.out.println(" "+nama+" melihat sumpit kiri..ternyata ada..");

System.out.println(" "+nama+ "sedang makan"); kanan.sinyal(); System.out.println(" "+nama+" melepas sumpit kanan"); kiri.sinyal(); System.out.println(" "+nama+" melepas sumpit kiri"); }

private String nama; private Semafor kiri, kanan;

* kelas ini merupakan implementasi semafor. * Implementasi ini merupakan modifikasi dari * implementasi semafor oleh Silberschatz, Galvin, * dan Gagne. */

final class Semafor {

public Semafor() {

nilai = 0; }

public Semafor(int n) {

nilai = n; }

public synchronized void tunggu() {

nilai--; if (nilai < 0) {

try

{ wait(); } catch(InterruptedException e) { System.exit(0); } }

} public synchronized void sinyal()

{ nilai++; if (nilai <= 0) notifyAll();

} private int nilai;

} Akan tetapi, solusi ini tidak dapat diterima. Alasannya sangat sederhana, yaitu deadlock dapat terjadi. Contoh

kasusnya adalah ketika semua filusuf telah mengambil sumpit di sebelah kirinya, maka tidak ada lagi sumpit yang tersisa di meja. Karena tidak ada sumpit yang tersisa di meja, maka setiap filusuf kekurangan sebuah sumpit. Berhubung setelah berhasil mengambil sebuah sumpit, sumpit tersebut tidak akan diletakkan kembali sampai filusuf yang bersangkutan makan, maka tidak akan ada satu filusuf pun yang akan makan.

Ada beberapa alternatif yang merupakan pemecahan dari permasalahan ini diantaranya yang disajikan oleh Stallings,antara lain dengan menyuruh setiap filusuf untuk mengambil sumpitnya secara berselang-seling. Filsuf dengan urutan bilangan ganjil pertama kali mengambil sumpit di sebelah kanannya,sementara filusuf dengan urutan bilangan genap pertama kali mengambil sumpit di sebelah kirinya sedangkan alternatif yang lain adalah dengan menyediakan sebanyak N sumpit lagi,sehingga lebih higienis di samping dijamin tidak menghasilkan deadlock ; mengurangi banyak filusuf yang boleh duduk di meja makan sebanyak satu.

Dokumen yang terkait

ANALISIS EFISIENSI PEMASARAN DAN PENDAPATAN USAHATANI ANGGUR (Studi Kasus di Kecamatan Wonoasih Kotamadya Probolinggo)

52 472 17

Studi Kualitas Air Sungai Konto Kabupaten Malang Berdasarkan Keanekaragaman Makroinvertebrata Sebagai Sumber Belajar Biologi

23 176 28

ANALISIS KOMPARATIF PENDAPATAN DAN EFISIENSI ANTARA BERAS POLES MEDIUM DENGAN BERAS POLES SUPER DI UD. PUTRA TEMU REJEKI (Studi Kasus di Desa Belung Kecamatan Poncokusumo Kabupaten Malang)

23 307 16

PENILAIAN MASYARAKAT TENTANG FILM LASKAR PELANGI Studi Pada Penonton Film Laskar Pelangi Di Studio 21 Malang Town Squere

17 165 2

Analisis Sistem Pengendalian Mutu dan Perencanaan Penugasan Audit pada Kantor Akuntan Publik. (Suatu Studi Kasus pada Kantor Akuntan Publik Jamaludin, Aria, Sukimto dan Rekan)

136 695 18

DOMESTIFIKASI PEREMPUAN DALAM IKLAN Studi Semiotika pada Iklan "Mama Suka", "Mama Lemon", dan "BuKrim"

133 700 21

PEMAKNAAN MAHASISWA TENTANG DAKWAH USTADZ FELIX SIAUW MELALUI TWITTER ( Studi Resepsi Pada Mahasiswa Jurusan Tarbiyah Universitas Muhammadiyah Malang Angkatan 2011)

59 326 21

STRATEGI KOMUNIKASI POLITIK PARTAI POLITIK PADA PEMILIHAN KEPALA DAERAH TAHUN 2012 DI KOTA BATU (Studi Kasus Tim Pemenangan Pemilu Eddy Rumpoko-Punjul Santoso)

119 459 25

STRATEGI PUBLIC RELATIONS DALAM MENANGANI KELUHAN PELANGGAN SPEEDY ( Studi Pada Public Relations PT Telkom Madiun)

32 284 52

FAKTOR-FAKTOR PENYEBAB ORANG TUA MENIKAHKAN ANAK PEREMPUANYA PADA USIA DINI ( Studi Deskriptif di Desa Tempurejo, Kecamatan Tempurejo, Kabupaten Jember)

12 105 72