やあ!CodeGym でマルチスレッドを勉強していると、「ミューテックス」と「モニター」という概念に頻繁に遭遇します。覗き込まずに、それらがどのように異なるかを言えますか? :) 「はい」の場合、よくやった!そうでないとしても(これが最も一般的です)、それは驚くべきことではありません。「ミューテックス」と「モニター」は実際には関連する概念です。さらに、他の Web サイトでマルチスレッドに関するレッスンを読んだりビデオを見たりすると、「セマフォ」という別の同様の概念に遭遇するでしょう。モニターやミューテックスとよく似た機能もあります。そのため、これら 3 つの用語について調査していきます。いくつかの例を見て、これらの概念が互いにどのように異なるかを最終的に理解します:)
つまり、共有リソースを操作できるのは一度に 1 つのスレッドだけです。他のスレッド (人) が占有されているリソースにアクセスしようとすると失敗します。ミューテックスにはいくつかの重要な機能があります。 まず、取り得る状態は「ロック解除」と「ロック」の 2 つだけです。これは、その仕組みを理解するのに役立ちます。ブール変数 (true/false) または 2 進数 (0/1) を使用して類似点を描くことができます。 、状態を直接制御することはできません。Java には、明示的にオブジェクトを取得し、そのミューテックスを取得し、必要なステータスを割り当てるメカニズムがありません。つまり、次のようなことはできません。
クラスによって表されます。セマフォ オブジェクトを作成するときは、次のコンストラクターを使用できます。
理解を容易にするために、少し簡略化して説明します。昼食を食べる必要がある 5 人の哲学者がいると想像してください。また、同時に2名様までご利用いただけるテーブルが1つございます。私たちの使命は、すべての哲学者に食事を提供することです。誰も空腹になってはならず、テーブルに座ろうとするときにお互いを「ブロック」してはいけません(デッドロックを回避する必要があります)。哲学者のクラスは次のようになります。
唯一の違いは、オブジェクトのミューテックスを取得できるのは一度に 1 つのスレッドのみであるのに対し、スレッド カウンターを使用するセマフォの場合は、複数のスレッドが同時にリソースにアクセスできることです。これは単なる偶然ではありません:) ミューテックスは実際にはセマフォですカウントは 1 です。言い換えれば、これは 1 つのスレッドに対応できるセマフォです。このカウンターは、1 (「ロック解除」) と 0 (「ロック」) の 2 つの値のみを持つことができるため、「バイナリ セマフォ」としても知られています。それでおしまい!ご覧のとおり、結局のところ、それほど混乱するものではありません :) さて、インターネットでマルチスレッドをさらに詳しく調べたい場合は、これらの概念を理解するのが少し簡単になります。次のレッスンでお会いしましょう!
ミューテックス
ミューテックス (またはロック) は、スレッドを同期するための特別なメカニズムです。1 つは Java のすべてのオブジェクトに「添付」されます。それはすでにご存知でしょう :) 標準クラスを使用するか、独自のクラスを作成するかは関係ありません。たとえば、 Cat と Dog : すべてのクラスのすべてのオブジェクトには、 mutexがあります。「ミューテックス」という用語は「MUTual EXclusion」に由来しており、その目的を完全に表しています。以前のレッスンで述べたように、ミューテックスを使用すると、一度に 1 つのスレッドのみがオブジェクトにアクセスできるようになります。 ミューテックスの一般的な実際の例には、トイレが含まれます。人がトイレの仕切りに入るときは、内側からドアを施錠します。トイレは、複数のスレッドからアクセスできるオブジェクトのようなものです。間仕切りドアの鍵はミューテックスのようで、外に並ぶ人の列は糸を表しています。ドアの鍵はトイレのミューテックスであり、1 人だけが中に入ることができます。
Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
これは、オブジェクトのミューテックスを解放できないことを意味します。Java マシンのみがそれに直接アクセスできます。プログラマは、言語のツールを通じてミューテックスを操作します。
モニター
モニターは、ミューテックス上の追加の「上部構造」です。実際、モニターはプログラマーには「見えない」コードの塊です。 先ほどミューテックスについて話したときに、簡単な例を挙げました。
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
}
}
}
synchronizedキーワード でマークされたコード ブロックで、 objオブジェクトのミューテックスが取得されます。ロックは取得できましたが、「保護」は具体的にどのように提供されるのでしょうか? synchronized という単語が表示された場合、他のスレッドがブロックに入るのを妨げるものは何でしょうか? 保護はモニターから来ます! コンパイラは、synchronizedキーワードをいくつかの特殊なコードに変換します。もう一度、doSomething()メソッドの例に戻りましょう。それに追加します:
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();
}
}
}
コンパイラがこのコードを変換した後、「内部で」何が起こるかを次に示します。
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;
}
}
もちろん、これは実際の例ではありません。ここでは、Java のようなコードを使用して、Java マシン内で何が起こっているかを示しました。とはいえ、この疑似コードは、同期ブロック内のオブジェクトとスレッドで実際に何が起こっているのか、そしてコンパイラがこのキーワードをプログラマには「見えない」いくつかのステートメントにどのように変換するのかをよく理解するのに役立ちます。基本的に、Java はsynchronizedキーワードを使用してモニターを表します。最後の例の synchronized キーワードの代わりに表示されるすべてのコードがモニターです。
セマフォ
マルチスレッドを個人的に研究する際に遭遇するもう 1 つの単語は、「セマフォ」です。これが何なのか、またモニターやミューテックスとどう違うのかを見てみましょう。セマフォは、あるリソースへのアクセスを同期するためのツールです。 特徴的なのは、カウンターを使用して同期メカニズムを作成していることです。 カウンタは、共有リソースに同時にアクセスできるスレッドの数を示します。 Java のセマフォはSemaphore
Semaphore(int permits)
Semaphore(int permits, boolean fair)
以下をコンストラクターに渡します。
- boolean Fair — スレッドがアクセスを取得する順序を確立します。Fairが trueの場合、待機中のスレッドへのアクセスは、要求された順序で許可されます。false の場合、順序はスレッド スケジューラによって決定されます。

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!");
}
}
}
プログラムを実行するコードは次のとおりです。
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();
}
}
「同時に食事できるのは 2 人の哲学者だけである」という条件を満たすために、カウンタが 2 に設定されたセマフォを作成しました。つまり、 PhilosopherクラスはThread を継承しているため、同時に実行できるスレッドは 2 つだけです。Semaphoreクラスのacquire()メソッドとrelease()メソッドは、そのアクセス カウンタを制御します。acquire() メソッドは、セマフォにリソースへのアクセスを要求します。カウンタが >0 の場合、アクセスが許可され、カウンタは 1 減ります。 release ()メソッドは、以前に許可されたアクセスを「解放」し、それをカウンタに返します (セマフォのアクセス カウンタを 1 増やします)。プログラムを実行すると何が得られるでしょうか? 問題は解決しましたか? 私たちの哲学者たちは順番を待つ間戦わないのだろうか?:) 得られたコンソール出力は次のとおりです。
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
やった!そして、タレスは一人で食事をしなければなりませんでしたが、私たちは彼を怒らせたとは思いません:) ミューテックスとセマフォの間にはいくつかの類似点があることに気づいたかもしれません。 実際、それらには、あるリソースへのアクセスを同期するという同じ使命があります。 
GO TO FULL VERSION