Hai! Saat Anda mempelajari multithreading di CodeGym, Anda sering menemukan konsep "mutex" dan "monitor". Tanpa mengintip, dapatkah Anda mengatakan perbedaannya? :) Jika ya, selamat! Jika tidak (ini yang paling umum), tidak mengherankan. "Mutex" dan "monitor" sebenarnya adalah konsep yang terkait. Selain itu, saat Anda membaca pelajaran dan menonton video tentang multithreading di situs web lain, Anda akan menemukan konsep serupa lainnya: "semaphore". Ini juga memiliki fungsi yang sangat mirip dengan monitor dan mutex. Itu sebabnya kami akan menyelidiki ketiga istilah ini. Kami akan melihat beberapa contoh dan sampai pada pemahaman yang pasti tentang bagaimana konsep-konsep ini berbeda satu sama lain :)
Mutex
Mutex (atau kunci) adalah mekanisme khusus untuk menyinkronkan utas. Satu "dilampirkan" ke setiap objek di Java — Anda sudah tahu itu :) Tidak masalah jika Anda menggunakan kelas standar atau membuat kelas Anda sendiri, misalnya Kucing dan Anjing : semua objek dari semua kelas memiliki mutex . Istilah "mutex" berasal dari "MUTual EXclusion", yang dengan sempurna menggambarkan tujuannya. Seperti yang kami katakan di salah satu pelajaran kami sebelumnya, mutex memungkinkan untuk memastikan bahwa hanya satu utas pada satu waktu yang memiliki akses ke objek. Contoh kehidupan nyata yang populer dari mutex melibatkan toilet. Ketika seseorang memasuki sekat toilet, dia mengunci pintu dari dalam. Toilet itu seperti benda yang bisa diakses oleh banyak utas. Kunci di pintu partisi seperti mutex, dan barisan orang di luar melambangkan utas. Kunci pintu adalah mutex toilet: memastikan hanya satu orang yang bisa masuk. Dengan kata lain, hanya satu utas pada satu waktu yang dapat bekerja dengan sumber daya bersama. Upaya oleh utas lain (orang) untuk mendapatkan akses ke sumber daya yang ditempati akan gagal. Mutex memiliki beberapa fitur penting. Pertama , hanya dua status yang memungkinkan: "tidak terkunci" dan "terkunci". Ini membantu kami memahami cara kerjanya: Anda dapat menggambar kesejajaran dengan variabel Boolean (benar/salah) atau bilangan biner (0/1). , negara tidak dapat dikontrol secara langsung. Java tidak memiliki mekanisme yang memungkinkan Anda mengambil objek secara eksplisit, mendapatkan mutexnya, dan menetapkan status yang diinginkan. Dengan kata lain, Anda tidak dapat melakukan sesuatu seperti:Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
Ini berarti Anda tidak dapat melepaskan mutex objek. Hanya mesin Java yang memiliki akses langsung ke sana. Pemrogram bekerja dengan mutex melalui alat bahasa.
Memantau
Monitor adalah "superstruktur" tambahan di atas mutex. Faktanya, monitor adalah potongan kode yang "tidak terlihat" oleh pemrogram. Ketika kami berbicara tentang mutex sebelumnya, kami memberikan contoh sederhana:public class Main {
private Object obj = new Object();
public void doSomething() {
// ...some logic, available for all threads
synchronized (obj) {
// Logic available to just one thread at a time
}
}
}
Di blok kode yang ditandai dengan kata kunci yang disinkronkan , mutex objek obj kita diperoleh. Bagus, kita bisa mendapatkan kuncinya, tapi bagaimana sebenarnya "perlindungan" itu diberikan? Saat kita melihat kata disinkronkan , apa yang mencegah utas lain memasuki blok? Perlindungan berasal dari monitor! Kompiler mengubah kata kunci yang disinkronkan menjadi beberapa bagian kode khusus. Sekali lagi, mari kita kembali ke contoh kita dengan metode doSomething() . Kami akan menambahkannya:
public class Main {
private Object obj = new Object();
public void doSomething() {
// ...some logic, available for all threads
// Logic available to just one thread at a time
synchronized (obj) {
/* Do important work that requires that the object
be accessed by only one thread */
obj.someImportantMethod();
}
}
}
Inilah yang terjadi "di bawah tenda" setelah kompiler mengonversi kode ini:
public class Main {
private Object obj = new Object();
public void doSomething() throws InterruptedException {
// ...some logic, available for all threads
// Logic available to just one thread at a time:
/* as long as the object's mutex is busy,
all the other threads (except the one that acquired it) are put to sleep */
while (obj.getMutex().isBusy()) {
Thread.sleep(1);
}
// Mark the object's mutex as busy
obj.getMutex().isBusy() = true;
/* Do important work that requires that the object
be accessed by only one thread */
obj.someImportantMethod();
// Free the object's mutex
obj.getMutex().isBusy() = false;
}
}
Tentu saja, ini bukan contoh nyata. Di sini, kami menggunakan kode mirip Java untuk menggambarkan apa yang terjadi di dalam mesin Java. Yang mengatakan, pseudo-code ini memberikan pemahaman yang sangat baik tentang apa yang sebenarnya terjadi dengan objek dan utas di dalam blok yang disinkronkan dan bagaimana kompiler mengubah kata kunci ini menjadi beberapa pernyataan yang "tidak terlihat" oleh pemrogram. Pada dasarnya, Java menggunakan kata kunci yang disinkronkan untuk mewakili monitor . Semua kode yang muncul sebagai pengganti kata kunci yang disinkronkan pada contoh terakhir adalah monitor.
Tiang sinyal
Kata lain yang akan Anda temui dalam studi pribadi Anda tentang multithreading adalah "semaphore". Mari kita cari tahu apa ini dan apa bedanya dengan monitor dan mutex. Semafor adalah alat untuk menyinkronkan akses ke beberapa sumber daya. Ciri khasnya adalah menggunakan penghitung untuk membuat mekanisme sinkronisasi. Penghitung memberi tahu kami berapa banyak utas yang dapat mengakses sumber daya bersama secara bersamaan. Semaphore di Java diwakili oleh kelas Semaphore . Saat membuat objek semafor, kita dapat menggunakan konstruktor berikut:Semaphore(int permits)
Semaphore(int permits, boolean fair)
Kami meneruskan yang berikut ke konstruktor:
- boolean fair — menetapkan urutan di mana utas akan mendapatkan akses. Jika adil benar, maka akses diberikan ke utas yang menunggu sesuai urutan yang mereka minta. Jika salah, maka urutan ditentukan oleh penjadwal utas.
class Philosopher extends Thread {
private Semaphore sem;
// Did the philosopher eat?
private boolean full = false;
private String name;
Philosopher(Semaphore sem, String name) {
this.sem=sem;
this.name=name;
}
public void run()
{
try
{
// If the philosopher has not eaten
if (!full) {
// Ask the semaphore for permission to run
sem.acquire();
System.out.println(name + " takes a seat at the table");
// The philosopher eats
sleep(300);
full = true;
System.out.println(name + " has eaten! He leaves the table");
sem.release();
// The philosopher leaves, making room for others
sleep(300);
}
}
catch(InterruptedException e) {
System.out.println("Something went wrong!");
}
}
}
Dan inilah kode untuk menjalankan program kami:
public class Main {
public static void main(String[] args) {
Semaphore sem = new Semaphore(2);
new Philosopher(sem, "Socrates").start();
new Philosopher(sem,"Plato").start();
new Philosopher(sem,"Aristotle").start();
new Philosopher(sem, "Thales").start();
new Philosopher(sem, "Pythagoras").start();
}
}
Kami membuat semafor yang penghitungnya disetel ke 2 untuk memenuhi syarat: hanya dua filsuf yang dapat makan pada waktu yang sama. Artinya, hanya dua utas yang dapat berjalan pada saat yang sama, karena kelas Filsuf kami mewarisi Thread ! Metode acquire () dan release() dari kelas Semaphore mengontrol penghitung aksesnya. Metode acquire() meminta semafor untuk akses ke sumber daya. Jika penghitung >0, maka akses diberikan dan penghitung dikurangi 1. Rilis ()metode "melepaskan" akses yang diberikan sebelumnya, mengembalikannya ke penghitung (meningkatkan penghitung akses semafor sebanyak 1). Apa yang kita dapatkan ketika kita menjalankan program? Apakah masalah terpecahkan? Apakah para filsuf kita tidak akan berperang sambil menunggu giliran? :) Inilah keluaran konsol yang kami dapatkan:
Socrates takes a seat at the table
Plato takes a seat at the table
Socrates has eaten! He leaves the table
Plato has eaten! He leaves the table
Aristotle takes a seat at the table
Pythagoras takes a seat at the table
Aristotle has eaten! He leaves the table
Pythagoras has eaten! He leaves the table
Thales takes a seat at the table
Thales has eaten! He leaves the table
Kita berhasil! Dan meskipun Thales harus makan sendirian, saya rasa kami tidak menyinggung perasaannya :) Anda mungkin telah memperhatikan beberapa kesamaan antara mutex dan semaphore. Memang, mereka memiliki misi yang sama: menyinkronkan akses ke beberapa sumber daya. Satu-satunya perbedaan adalah bahwa mutex objek dapat diperoleh hanya dengan satu utas pada satu waktu, sedangkan dalam kasus semaphore, yang menggunakan penghitung utas, beberapa utas dapat mengakses sumber daya secara bersamaan. Ini bukan hanya kebetulan :) Mutex sebenarnya adalah semafordengan hitungan 1. Dengan kata lain, itu adalah semafor yang dapat menampung satu utas. Ini juga dikenal sebagai "semafor biner" karena penghitungnya hanya dapat memiliki 2 nilai — 1 ("tidak terkunci") dan 0 ("terkunci"). Itu dia! Seperti yang Anda lihat, ini tidak terlalu membingungkan :) Sekarang, jika Anda ingin mempelajari multithreading lebih detail di Internet, akan lebih mudah bagi Anda untuk menavigasi konsep-konsep ini. Sampai jumpa di pelajaran selanjutnya!