Bab 27. Sinkronisasi Dengan Semafor
27.1. Pendahuluan
Sinkronisasi dua arah adalah suatu mekanisme dimana suatu thread dapat mengendalikan sinkronisasi thread lain, begitu pula sebaliknya. Berikut ini adalah sebuah program yang menggunakan proses sinkronisasi dua arah. Program yang dibuat dalam bahasa Java ini, bernama Hompimpah. Contoh program sinkronisasi dua arah adalah semafor berjenis non-spinlock. Semafor jenis non-spinlock mengimplementasikan kelas Semafor, dimana semafor yang digunakan adalah jika ada proses yang sedang berjalan, maka proses lain akan menunggu, sampai ada thread lain yang memanggilnya. Hal ini sesuai dengan prinsip critical section yaitu mutualy exclusive , hanya satu thread yang diizinkan mengakses critical section; progress, critical section pasti ada yang mengakses; bounded waiting, setiap thread dijamin dapat meng-akses critical section. Hal ini dapat dilihat dalam program yaitu ketika sang bandar sedang berada pada critical section, pemain akan menunggu hingga bandar selesai mengaksesnya. Sedangkan kebalikannya adalah semafor jenis spinlock , yang akan melakukan infinite loop , sehingga thread tersebut tidak ada jaminan akan masuk critical section . Semafor jenis non-spinlock ini terdiri dari 2 operasi untuk proses sinkronisasi, yaitu operasi buka(),dan operasi kunci() .
27.2. Penggunaan Semafor
Program ini berjalan layaknya permainan Hompimpah yang telah kita kenal. Adapun yang dilakukan dalam permainan hompimpah adalah sebagai berikut:
1. Peranan
Gambar 27.1. Peranan yang terdapat dalam permainan
Dalam permainan Hompimpah kita mengenal dua peranan, yaitu pemain-pemain yang melakukan hompimpah (gambar sebelah kanan yang berjumlah 5) dan seorang bandar (gambar sebelah kiri), yang berfungsi menetukan, kapan permainan dimulai dan kapan permainan berakhir. Dalam hal ini bandar bertindak sebagai pengatur jalannya permainan.
2. Memulai permainan
Permainan mulai ketika bandar menyerukan Hompimpah. Ketika itu bandar akan meminta pemain untuk melakukan hompimpah satu per satu(tidak serentak). Dalam program ini, pemain tidak gambreng secara bersamaan seperti gambreng pada umumnya, melainkan satu per satu dengan pengaturan dari Semafor.Kemudian bandar akan menyerukan gambreng sebagai penanda hompimpah berhenti, sehingga pemain pun menghentikan hompimpah dan masing-masing akan menunjukan tangannya, apakah telapak atau punggung.
3. Pengecekan pemenang
Gambar 27.3. Bandar memeriksa pemenang
Terlihat pada gambar, setelah bandar menyerukan gambreng dan hompimpah berhenti, bandar akan melakukan perhitungan banyaknya telapak dan punggung. Apabila bandar menemukan ada diantara telapak atau punggung yang berjumlah satu, maka satu-satunya pemain itu akan keluar sebagai pemenang. Namun apabila tidak ada diantara telapak atau punggung yang berjumlah satu, seperti pada gambar maka bandar akan mengulang permainan dengan menyerukan gambreng. Hal tersebut akan berulang terus-menerus hingga terdapat 1 tangan yang berbeda Dengan kata lain, bandar akan terus menyerukan gambreng dan mengulang permainan hingga menemukan pemenang.
Gambar 27.4. Bandar mengulang gambreng
Seperti halnya permainan Hompimpah yang diilustrasikan di atas, program Hompimpah juga berjalan demikian layaknya permainan Hompimpah sebenarnya. Tetapi, ada perbedaan terhadap jumlah bandarnya. Program Hompimpah memiliki jumlah bandar yang sama dengan jumlah pemainnya, sehingga setiap pemain memiliki satu bandar. Adapun tujuan sinkronisasi dua arah adalah mengendalikan thread-thread sesuai dengan keinginan kita melalui kendali bandarGambreng() yang diimplementasikan pada program Hompimpah.
27.3. Program
Contoh 27.1. Program yang menggunakan proses sinkronisasi dua arah
001 public class Hompimpah 002 {
003
004 private static final int JUMLAH_PEMAIN=6;
005 public static void main(String args[])throws Exception 006 {
007 Gambreng gamserver=new Gambreng(JUMLAH_PEMAIN); 008 Thread[] pemain=new Thread[JUMLAH_PEMAIN]; 009 for (int ii=0; ii<JUMLAH_PEMAIN; ii++) 010 {
011 pemain[ii]= new Thread (new Pemain(gamserver, ii)); 012 pemain[ii].start(); 013 014 } 015 gamserver.bandarGambreng(); 017 } 018 019 } 020
021 class Pemain implements Runnable 022 {
023 Pemain(Gambreng gserver, int nomer) 024 {
025 gamserver=gserver; 026 no_pemain=nomer; 027 }
028
029 public void run() 030 {
031 gamserver.pemainGambreng(no_pemain); 032 }
033
034 private Gambreng gamserver; 035 private int no_pemain; 036
037 } 038
039 class Gambreng 040 {
041 private boolean adaPemenang;
042 private int truePemain, trueCount, iterasiGambreng; 043 private int falsePemain, falseCount;
044 private int nomorPemenang, jumlahPemain; 045 private Semafor[] bandar, pemain;
046 private Semafor mutex; 047
048 public Gambreng(int jumlah) 049 {
050 bandar=new Semafor[jumlah]; 051 pemain=new Semafor[jumlah]; 052 for(int ii=0; ii<jumlah;ii++) 053 { 054 bandar[ii]=new Semafor(); 055 pemain[ii]=new Semafor(); 056 057 } 058 060 mutex=new Semafor(1); 061 jumlahPemain=jumlah; 062 iterasiGambreng=0; 063 resetGambreng(); 064 }
065 public void bandarGambreng() 066 { 067 syncBandar(); 068 while(!menangGambreng()) 069 { 070 resetGambreng(); 071 syncPemainBandar();
072 hitungGambreng(); 073 iterasiGambreng++; 074 }
075 syncPemain();
076 System.out.println("Nomor Peserta Pemain [0] - ["+
(jumlahPemain-1)+"] Pemenang Pemain nomor["+ nomorPemenang +"] Jumlah Iterasi["+ iterasiGambreng +"]");
077 }
078 public void pemainGambreng (int nomor) 079 { 080 syncBandarPemain(nomor); 081 while(!menangGambreng()) 082 { 083 mutex.kunci(); 084 085 if((int)(Math.random()*2)==1) 086 { 087 truePemain=nomor; 088 trueCount++; 089 090 } 091 else 092 { 093 falsePemain=nomor; 094 falseCount++; 095 } 096 mutex.buka(); 097 syncBandarPemain(nomor); 098 } 099 }
100 public void resetGambreng() 101 { 102 mutex.kunci(); 103 adaPemenang=false; 104 truePemain=0; 105 trueCount=0; 106 falsePemain=0; 107 falseCount=0; 108 109 mutex.buka(); 110 } 111
112 private boolean menangGambreng() 113 {
114 return adaPemenang; 115 }
116 private void hitungGambreng() 117 { 118 mutex.kunci(); 119 120 if(trueCount==1) 121 { 122 adaPemenang=true; 123 nomorPemenang=truePemain; 124 } 125 else if(falseCount==1) 126 { 127 adaPemenang=true; 128 nomorPemenang=falsePemain;
129 }
130 mutex.buka(); 131 }
132 private void syncPemainBandar() 133 {
134 for(int ii=0; ii<jumlahPemain;ii++) 135 { 136 pemain[ii].buka(); 137 bandar[ii].kunci(); 138 } 139 } 140
141 private void syncBandar() 142 { 143 for(int ii=0;ii<jumlahPemain;ii++) 144 { 145 bandar[ii].kunci(); 146 } 147 }
148 private void syncPemain() 149 { 150 for(int ii=0;ii<jumlahPemain;ii++) 151 { 152 pemain[ii].buka(); 153 } 154 }
155 private void syncBandarPemain(int ii) 156 { 157 bandar[ii].buka(); 158 pemain[ii].kunci(); 159 } 160 161 162 } 163 164 class Semafor 165 { 166 public Semafor() 167 { 168 value=0; 169 }
170 public Semafor(int val) 171 {
172 value=val; 173 }
174 public synchronized void kunci() 175 { 176 while(value==0) 177 { 178 try 179 { 180 wait(); 181 182 } 183 catch(InterruptedException e) 184 { 185 } 186 value--; 187 }
188 189 }
190 public synchronized void buka() 191 {
192 value++; 193 notify(); 194 }
195 private int value; 196 }
27.4. Penjelasan Program
Program Hompimpah terdiri dari 4 class yaitu class Hompimpah, class Pemain, class Gambreng,class Semafor. Masing-masing class berfungsi sebagai berikut:
1. Class Hompimpah: dalam kelas ini program Hompimpah dapat dijalankan
2. Class Gambreng: dalam kelas ini seluruh proses permainan Hompimpah dijalankan. 3. Class Semafor: kelas ini menjalankan proses sinkronisasi.
4. Class Hompimpah: dalam kelas terdapat fungsi run() dengan fungsi ini tiap-tiap pemain akan dihidupkan.
Penjelasan selengkapnya sebagai berikut:
Contoh 27.2. Class Hompimpah
001 public class Hompimpah 002 {
003
004 private static final int JUMLAH_PEMAIN=6;
005 public static void main(String args[])throws Exception 006 {
007 Gambreng gamserver=new Gambreng(JUMLAH_PEMAIN); 008 Thread[] pemain=new Thread[JUMLAH_PEMAIN]; 009 for (int ii=0; ii<JUMLAH_PEMAIN; ii++) 010 {
011 pemain[ii]= new Thread (new Pemain(gamserver, ii)); 012 pemain[ii].start(); 013 014 } 015 gamserver.bandarGambreng(); 017 } 018 019 } 020
Pada class Hompimpah, dibuat objek baru dari class Gambreng (baris 7), dimana dalam kelas tersebut dibuat 2 jenis thread yaitu thread bandar dan thread pemain. Masing-masing thread adalah array dari objek Semafor, yang berkapasitas JUMLAH_PEMAIN, yaitu 6. Kemudian kedua jenis thread di set value-nya sebagai 0 (terdapat pada
class Gambreng). Hal ini mengindikasikan bahwa thread bandar dan pemain sedang tidur. Dalam kelas Gambreng,
mutex sebagai objek dari class Semafor, di set 1,yang berarti bahwa critical section sedang dalam keadaan
available atau tidak ada thread yang sedang memakai. Kemudian iterasiGambreng di set 0 dan pemanggilan method resetGambreng() pada class Gambreng sebagai penanda awal permainan Pada baris ke 8 class
Hompimpah dibuat array object baru dari class Thread yaitu pemain sebanyak JUMLAH_PEMAIN yang di set 6. Tiap-tiap thread memiliki no_pemain dan memiliki objek dari class Gambreng. Selanjutnya thread- thread
tersebut menjalankan method run()-nya, dimana dalam method tersebut tiap-tiap thread pemain mengirimkan
Contoh 27.3. method pemainGambreng
078 public void pemainGambreng (int nomor) 079 { 080 syncBandarPemain(nomor); 081 while(!menangGambreng()) 082 { 083 mutex.kunci(); 084 085 if((int)(Math.random()*2)==1) 086 { 087 truePemain=nomor; 088 trueCount++; 089 090 } 091 else 092 { 093 falsePemain=nomor; 094 falseCount++; 095 } 096 mutex.buka(); 097 syncBandarPemain(nomor); 098 } 099 }
100 public void resetGambreng() 101 { 102 mutex.kunci(); 103 adaPemenang=false; 104 truePemain=0; 105 trueCount=0; 106 falsePemain=0; 107 falseCount=0; 108 109 mutex.buka(); 110 } 111
Pada fungsi tersebut tiap-tiap thread akan mengakses critical section secara bergantian, sesuai dengan prinsip
mutual exclusion . Di dalam critical section , thread-thread akan dicek apakah truePemain(telapak) atau
falsePemain(punggung) kemudian dihitung berapa jumlah telapak dan punggungnya. Proses ini akan terus me-loop sampai ada pemenang. Sebelum memasuki critical section dan melakukan pengecekan, sistem melakukan proses sinkronisasi bandar dan pemain, dengan memangil fungsi syncBandarPemain()
Contoh 27.4. syncBandarPemain
155 private void syncBandarPemain(int ii) 156 {
157 bandar[ii].buka(); 158 pemain[ii].kunci(); 159 }
160
dalam fungsi tersebut bandar melepaskan kunci, dan memanggil thread pemain dengan notify() yang
merupakan bagian dari fungsi buka() dari class Semafor, untuk kemudian thread tersebut dapat mengakses
critical section. Setelah seluruh thread pemain mengakses critical section dan melakukan pengecekan, program
memanggil fungsi bandarGambreng() pada class Gambreng,untuk melakukan proses sinkronisasi bandar
Contoh 27.5. syncBandar
141 private void syncBandar() 142 { 143 for(int ii=0;ii<jumlahPemain;ii++) 144 { 145 bandar[ii].kunci(); 146 } 147 }
, sehingga bandar terkunci dalam critical section dan memegang kekuasaan dalam Gambreng untuk melakukan perulangan fungsi-fungsi berikut, yang masing-masing akan dipanggil dalam fungsi bandarGambreng() :
1. Me-reset permainan gambreng dengan memanggil fungsi resetGambreng().
Contoh 27.6. resetGambreng
100 public void resetGambreng() 101 { 102 mutex.kunci(); 103 adaPemenang=false; 104 truePemain=0; 105 trueCount=0; 106 falsePemain=0; 107 falseCount=0; 108 109 mutex.buka(); 110 } 111
Dalam fungsi resetGambreng(), truePemain, falsePemain, trueCount dan falseCount = 0,
kemudian ada pemenang diset false, sebagai tanda awal dari permainan.
2. Melakukan sinkronisasi pemain dan bandar dengan memanggil fungsi syncPemainBandar().
Contoh 27.7. syncPemainBandar
132 private void syncPemainBandar() 133 {
134 for(int ii=0; ii<jumlahPemain;ii++) 135 {
136 pemain[ii].buka(); 137 bandar[ii].kunci(); 138 }
139 }
Ketika melakukan pemanggilan fungsi ini, seluruh pemain dibangunkan kemudian tiap-tiap bandar akan tidur. Dengan fungsi ini, bandar akan tetap menunggu sampai semua pemain membangunkannya. Dan ketika bandar bangun, pemain akan ditidurkan kembali.
3. Melakukan penghitungan dengan fungsi hitungGambreng()
Contoh 27.8. hitungGambreng
116 private void hitungGambreng() 117 {
118 mutex.kunci(); 119
120 if(trueCount==1) 121 {
122 adaPemenang=true; 123 nomorPemenang=truePemain; 124 } 125 else if(falseCount == 1) 126 { 127 adaPemenang=true; 128 nomorPemenang=falsePemain; 129 } 130 mutex.buka(); 131 }
Dengan fungsi ini, kembali melakukan pengaksesan variable. Dengan melakukan pengecekan, apabila salah satu diantara trueCount atau falseCount bernilai 1, maka adaPemenang. Nilai ini akan diproses oleh fungsi
menangGambreng(), yang akan mengembalikan nilai boolean(true atau false). Jika fungsi tersebut bernilai true
maka proses loop berhenti.
4. Menghitung iterasi perulangan, sampai fungsi menangGambreng() mengembalikan nilai true, yang
menandakan adanya pemenang.
Saat proses perulangan berhenti, pemain melepaskan kunci kemudian sistem menampilkan output saat execute, berupa no_pemain yang keluar sebagai pemenang. Setiap proses execute akan menampilkan nomor pemain
yang berbeda-beda , karena proses random dan urutan pengaksesan critical section oleh thread pada tiap peng-
execute-an berbeda-beda.
Contoh 27.9. Keluaran Program
Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[3] Jumlah Iterasi[12] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[2] Jumlah Iterasi[1] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[0] Jumlah Iterasi[6] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[4] Jumlah Iterasi[2] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[1] Jumlah Iterasi[14] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[3] Jumlah Iterasi[9] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[5] Jumlah Iterasi[3] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[3] Jumlah Iterasi[2] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[4] Jumlah Iterasi[5] Nomor Peserta Pemain [0] - [5] Pemenang Pemain nomor[0] Jumlah Iterasi[11]
Contoh di atas menunjukkan hasil Hompimpah tidak selalu sama. Jumlah iterasi menunjukkan berapa kali bandar menyerukan gambreng dan mengulang permainan.
27.5. Rangkuman
Program Hompimpah merupakan ilustrasi dimana sebuah thread memegang kendali sinkronisasi thread lainnya. Seperti yang dijelaskan dalam program masing-masing dari pemain saling mengendalikan satu sama lain, dengan menggunakan alat sinkronisasi yang bernama semafor. Semafor dalam program adalah semafor buatan berupa class Semafor yang dibuat dalam bahasa Java. Adapun di dalamnya terdapat 2 fungsi yaitu fungsi buka() dan fungsi kunci() dengan
fungsi-fungsi inilah masing-masing thread dapat mengendalikan satu sama lain
Rujukan
[Silberschatz2005] Avi Silberschatz, Peter Galvin, dan Grag Gagne. 2005. Operating Systems
Appendix
27.6. Penjelasan Alur Program
1. Mula - mula semua bandar dan pemain tidur2. Dibuat thread Pemain sebanyak JUMLAH_PEMAIN yang kemudian akan menjalankan run() secara terpisah. 3. Setiap thread Pemain yang memanggil pemainGambreng akan terhambat pada method syncBandarPemain(nomor), ketika semua pemain sudah menjalankan method ini, semua bandar dalam keadaan bangun(buka).
4. bandarGambreng (baris 15) dijalankan
5. Karena syncPemainBandar() dijalankan maka thread Pemain yang tadinya terhambat dapat jalan.
6. Setelah semua Pemain sudah gambreng (yang kemudian akan terhambat di syncBandarPemain(nomor) di bawah), maka akan hitungGambreng().
7. Jika !MenangGambreng, Pemain akan di buka lagi.