こんにちは!今日は引き続きマルチスレッド プログラミングの機能を検討し、スレッドの同期について話します。
「同期」とは何ですか?プログラミングの範囲外では、これは 2 つのデバイスまたはプログラムが連携できるようにするある種のセットアップを指します。例えば、スマートフォンやパソコンをGoogleアカウントと同期したり、Webサイト上の個人アカウントとSNS上のアカウントを同期してログインしたりすることができます。スレッドの同期にも同様の意味があり、スレッドが相互に対話する方法を設定します。これまでの講義では、スレッドは互いに別々に存在し、動作していました。1 人は何かを数えていて、2 人目は寝ていて、3 人目はコンソールに何かを表示していましたが、互いに対話することはありませんでした。実際のプログラムではそのような状況はまれです。たとえば、同じデータセットを処理して、その中の何かを変更するなど、複数のスレッドがアクティブに動作することができます。これにより問題が発生します。複数のスレッドがテキスト ファイルやコンソールなどの同じ場所にテキストを書き込んでいると想像してください。この場合、このファイルまたはコンソールは共有リソースになります。スレッドは互いの存在を知らないため、スレッド スケジューラが割り当てた時間内で管理できるすべてのことを単に書き留めます。このコースの最近の講義で、これがどのような結果をもたらすかを示す例がありました。思い出してください。その 理由は、スレッドが相互にアクションを調整せずに、共有リソースであるコンソールで動作したという事実にあります。スレッド スケジューラが Thread-1 に時間を割り当てた場合、すぐにすべてをコンソールに書き込みます。他のスレッドがすでに書き込めたもの、または書き込めなかったものは重要ではありません。結果はご覧のとおり、悲惨なものです。したがって、マルチスレッドプログラミングでは、特別な概念のミューテックスが導入されました(英語の「ミューテックス」、「相互排他」-「相互排他」から)。 ミューテックスの目的は、特定の時点で 1 つのスレッドだけがオブジェクトにアクセスできるようにするメカニズムを提供することです。Thread-1 がオブジェクト A のミューテックスを取得した場合、他のスレッドはオブジェクト A にアクセスしてその内容を変更することはできません。オブジェクト A のミューテックスが解放されるまで、残りのスレッドは待機することになります。実際の例: あなたと他の 10 人の見知らぬ人がトレーニングに参加していると想像してください。交代でアイデアを表現したり、何かについて話し合ったりする必要があります。しかし、お互いに初めて会うので、常にお互いの邪魔をしたり、喧騒に陥ったりしないように、「トーキングボール」ルールを使用します。つまり、1 人だけが話すことができます。ボールを持っている人だけです。彼の手。このようにして、議論は適切かつ実りあるものになるでしょう。したがって、ミューテックスは本質的にそのようなボールです。オブジェクトのミューテックスが 1 つのスレッドの手にある場合、他のスレッドはそのオブジェクトにアクセスできなくなります。ミューテックスを作成するために何もする必要はありません。ミューテックスはすでにクラスに組み込まれています
Object
。つまり、Java のすべてのオブジェクトがミューテックスを持っています。
Java での同期オペレーターの仕組み
新しいキーワード「synchronized 」について見てみましょう。これはコードの特定の部分をマークします。コードのブロックが synchronized キーワードでマークされている場合、そのブロックは一度に 1 つのスレッドによってのみ実行できることを意味します。同期はさまざまな方法で実装できます。たとえば、同期メソッド全体を作成します。public synchronized void doSomething() {
//...method logic
}
または、オブジェクトに対して同期が実行されるコード ブロックを作成します。
public class Main {
private Object obj = new Object();
public void doSomething() {
//...some logic available to all threads
synchronized (obj) {
//logic that is only available to one thread at a time
}
}
}
意味は簡単です。あるスレッドが synchronized という単語でマークされたコード ブロックに入ると、そのスレッドは即座にオブジェクトのミューテックスを取得し、同じブロックまたはメソッドに入ろうとする他のすべてのスレッドは、前のスレッドが作業を完了してそのメソッドを解放するまで待機することになります。モニター。 ところで!コースの講義ですでに同期の例を見てきましたが、それらは異なっていました。
public void swap()
{
synchronized (this)
{
//...method logic
}
}
このトピックはあなたにとって新しいものなので、当然最初は構文に混乱するでしょう。したがって、後の書き方で混乱しないように、すぐに覚えてください。これら 2 つの記述方法は同じ意味です。
public void swap() {
synchronized (this)
{
//...method logic
}
}
public synchronized void swap() {
}
}
最初のケースでは、メソッドに入った直後に、同期されたコード ブロックを作成します。これは object this
、つまり現在のオブジェクトによって同期されます。2 番目の例では、メソッド全体に synchronized という単語を入れています。同期が実行されるオブジェクトを明示的に指定する必要はなくなりました。メソッド全体が単語でマークされると、このメソッドはクラスのすべてのオブジェクトに対して自動的に同期されます。どちらの方法が優れているかについては、これ以上深く議論するのはやめましょう。今のところ、一番好きなものを選択してください:) 重要なことは、メソッド内のすべてのロジックが 1 つのスレッドによって同時に実行される場合にのみ、同期されたメソッドを宣言できることを覚えておくことです。たとえば、この場合、doSomething()
メソッドを同期させるとエラーになります。
public class Main {
private Object obj = new Object();
public void doSomething() {
//...some logic available to all threads
synchronized (obj) {
//logic that is only available to one thread at a time
}
}
}
ご覧のとおり、メソッドの一部には同期が不要なロジックが含まれています。そのコードは複数のスレッドで同時に実行でき、すべての重要な場所は別の同期ブロックに割り当てられます。そして一瞬。名前を交換した講義の例を顕微鏡で見てみましょう。
public void swap()
{
synchronized (this)
{
//...method logic
}
}
注意:同期は を使用して実行されますthis
。つまり、特定のオブジェクトに対してですMyClass
。Thread-1
2 つのスレッド (とThread-2
) と 1 つのオブジェクトだけがあると想像してくださいMyClass myClass
。この場合、Thread-1
メソッドが呼び出されるとmyClass.swap()
、オブジェクトのミューテックスはビジー状態になり、メソッドThread-2
を呼び出そうとすると、myClass.swap()
ミューテックスが空くのを待ってハングします。2 つのスレッドと 2 つのオブジェクトがあり、しかも異なるオブジェクト上にある場合MyClass
、myClass1
スレッドmyClass2
は同期されたメソッドを簡単に同時に実行できます。最初のスレッドは次のことを行います。
myClass1.swap();
2 番目のものは次のことを行います。
myClass2.swap();
この場合、synchronized キーワードはメソッド内にあります。swap()
特定のオブジェクトに対して実行されるため、メソッド内の synchronized キーワードはプログラムの動作には影響しません。後者の場合、オブジェクトが 2 つあるため、スレッドが相互に問題を引き起こすことはありません。結局のところ、2 つのオブジェクトには 2 つの異なるミューテックスがあり、それらのキャプチャは相互に依存しません。
静的メソッドの同期の機能
しかし、静的メソッドを同期する必要がある場合はどうすればよいでしょうか?class MyClass {
private static String name1 = "Olya";
private static String name2 = "Lena";
public static synchronized void swap() {
String s = name1;
name1 = name2;
name2 = s;
}
}
この場合、何がミューテックスとして機能するかは不明です。結局のところ、すべてのオブジェクトにはミューテックスがあることがすでに決定されています。しかし問題は、静的メソッドを呼び出すのにMyClass.swap()
オブジェクトが必要ないことです。メソッドは静的です。それで、次は何でしょうか?:/ 実際には、これで問題ありません。Java の作成者がすべてを処理しました :) 重要な「マルチスレッド」ロジックを含むメソッドが静的である場合、同期はクラスごとに実行されます。より明確にするために、上記のコードは次のように書き換えることができます。
class MyClass {
private static String name1 = "Olya";
private static String name2 = "Lena";
public static void swap() {
synchronized (MyClass.class) {
String s = name1;
name1 = name2;
name2 = s;
}
}
}
原則として、これは自分で考えることができます。オブジェクトがないため、同期メカニズムは何らかの方法でクラス自体に「組み込まれている」必要があります。そのとおりです。クラス間で同期することもできます。
GO TO FULL VERSION