同期されたキーワード
同期して、ロックを呼び出し、主にメソッドとコードブロックをロックするために使用されます。メソッドまたはコードブロックが同期されている場合、最大で1つのスレッドが同時にコードを実行しています。複数のスレッドが同じオブジェクトのロックメソッド/コードブロックにアクセスすると、1つのスレッドのみが同時に実行され、残りのスレッドは現在のスレッドが実行する前にコードセグメントを実行するのを待つ必要があります。ただし、残りのスレッドは、オブジェクト内のロックされていないコードブロックにアクセスできます。
同期されたのは、主に同期メソッドと同期ブロックの2つの方法が含まれています。
同期された方法
同期されたキーワードをメソッド宣言に追加して、同期されたメソッドを宣言します。のように:
public同期void getResult();
同期されたメソッドは、クラスメンバー変数へのアクセスを制御します。クラスメンバー変数のアクセス制御をどのように回避しますか?このメソッドは、同期されたキーワードを使用して、メソッドがロックされていることを示すことを知っています。スレッドが変更された方法にアクセスする場合、他のスレッドが「排他的」であるかどうかを判断する必要があります。各クラスインスタンスはロックに対応します。同期された各メソッドは、メソッドを実行する前にクラスインスタンスのロックを呼び出す必要があります。それ以外の場合、それが属するスレッドがブロックされます。メソッドが実行されると、ロックは排他的になります。ロックはメソッドから戻るまで放出されず、ブロックされたスレッドはロックを取得できます。
実際、同期された方法には欠陥があります。大きな方法を同期していると宣言すると、効率に大きく影響します。複数のスレッドが同期されたメソッドにアクセスしている場合、1つのスレッドのみが同時にメソッドを実行していますが、他のスレッドは待つ必要があります。ただし、メソッドが同期して使用しない場合、すべてのスレッドが同時に実行でき、総実行時間が短縮されます。したがって、メソッドが複数のスレッドで実行されないことやリソース共有の問題がないことがわかっている場合、同期されたキーワードを使用する必要はありません。ただし、同期されたキーワードを使用する必要がある場合は、同期化されたコードブロックで同期されたメソッドを置き換えることができます。
同期ブロック
同期コードブロックは、同期されたメソッドと同じ役割を再生しますが、重要な領域を可能な限り短くすることを除きます。言い換えれば、必要な共有データのみを保護し、残りの長いコードブロックのこの操作を残します。構文は次のとおりです。
同期(オブジェクト){//アクセス制御を許可するコード}この方法で同期キーワードを使用する必要がある場合、オブジェクト参照をパラメーターとして使用する必要があります。通常、私たちはこのパラメーターをthis.synchronized(this){//アクセス制御を許可するコード}としてよく使用します}同期された(これ)の次の理解があります。
1. 2つの同時スレッドが同じオブジェクトオブジェクトでこの同期(この)同期コードブロックにアクセスすると、1回以内に1つのスレッドのみを実行できます。別のスレッドは、コードブロックを実行する前に、現在のスレッドがこのコードブロックを実行するのを待つ必要があります。
2.ただし、1つのスレッドがオブジェクトの1つの同期(この)同期コードブロックにアクセスすると、別のスレッドはオブジェクトの非同期(この)同期コードブロックにアクセスできます。
3.スレッドがオブジェクトの同期(この)同期コードブロックにアクセスすると、他のスレッドがオブジェクト内の他のすべての同期(この)同期コードブロックにアクセスするのをブロックすることが特に重要です。
4. 3番目の例は、他の同期コードブロックにも適用されます。つまり、スレッドがオブジェクトの同期(この)同期コードブロックにアクセスすると、このオブジェクトのオブジェクトロックが取得されます。その結果、オブジェクトオブジェクトのすべての同期コード部分への他のスレッドアクセスが一時的にブロックされます。
ロック
Java Multithreadingには「最初に来て、後で来る」原則があります。つまり、最初にキーをつかむ人は誰でも最初にそれを使用します。リソース競争の問題を回避するために、Javaは回避するために同期メカニズムを使用し、同期メカニズムがロックの概念を使用して制御されることを知っています。では、ロックはJavaプログラムにどのように反映されていますか?ここでは、2つの概念を把握する必要があります。
ロックとは何ですか?日常生活では、それはドア、箱、引き出し、その他のオブジェクトに追加されたシーラーであり、他の人がのぞきや盗みを防ぎ、保護的な役割を果たします。 Javaでも同じことが言えます。ロックは、オブジェクトの保護に役割を果たします。スレッドが特定のリソースのみを占める場合、他のスレッドを使用したくないが、それらを使用したいですか?使い終わったら話しましょう!
環境を実行しているJavaプログラムでは、JVMは2種類のスレッドで共有されるデータを調整する必要があります。
1.ヒープに保存されたインスタンス変数
2。メソッド領域に保存されているクラス変数。
Java仮想マシンでは、各オブジェクトとクラスはモニターに論理的に関連付けられています。オブジェクトの場合、関連するモニターはオブジェクトのインスタンス変数を保護します。クラスの場合、モニターはクラス変数を保護します。オブジェクトにインスタンス変数がない場合、またはクラスに変数がない場合、関連するモニターは何も監視しません。
モニターの排他的監視機能を実装するために、Java Virtual Machineは、各オブジェクトとクラスのロックを関連付けます。 1つのスレッドのみがいつでも持つことができる特権を表します。スレッドは、インスタンス変数やクラス変数にアクセスするときにロックする必要はありません。スレッドがロックを取得した場合、他のスレッドがロックを解放する前に同じロックを取得することは不可能です。スレッドは同じオブジェクトを複数回ロックできます。各オブジェクトについて、Java仮想マシンはロックカウンターを維持します。スレッドがオブジェクトを取得するたびに、カウンターは1増加し、リリースされるたびにカウンターが1削減されます。カウンター値が0の場合、ロックは完全に解放されます。
Javaプログラマーは自分でロックを追加する必要はありません。オブジェクトロックはJava仮想マシンで内部で使用されます。 Javaプログラムでは、同期ブロックまたは同期されたメソッドを使用して監視領域をマークする必要があります。監視エリアに入るたびに、Java仮想マシンはオブジェクトまたはクラスを自動的にロックします。
シンプルなロック
Synchronizedを使用する場合、次のようなロックを使用します。
public class threadtest {public void test(){synchronized(this){// do Something}}}}同期すると、1つのスレッドのみが同時に行われているスレッドのみが保証されます。同期する代わりにロックの使用は次のとおりです。
パブリッククラススレッドテスト{lock lock = new Lock(); public void test(){lock.lock(); //何かlock.unlock()を実行します; }}lock()メソッドはロックインスタンスオブジェクトをロックするため、オブジェクトのロック()メソッドを呼び出すすべてのスレッドは、ロックオブジェクトのロック()メソッドが呼び出されるまでブロックされます。
ロックされているものは何ですか?
この質問の前に、明確にする必要があります。同期されたキーワードがメソッドまたはオブジェクトに追加されるかどうかにかかわらず、取得するロックはオブジェクトです。 Javaでは、すべてのオブジェクトをロックとして使用できます。これは、主に次の3つの側面に反映されています。
同期方法の場合、ロックは現在のインスタンスオブジェクトです。
同期メソッドブロックの場合、ロックは同期された括弧で構成されたオブジェクトです。
静的同期方法の場合、ロックは現在のオブジェクトのクラスオブジェクトです。
まず、次の例を見てみましょう。
public class threadtest_01はrunnable {@override public synchronized void run(){for(int i = 0; i <3; i ++){system.out.println(thread.currentthread()。getName()+"run ....."); }} public static void main(string [] args){for(int i = 0; i <5; i ++){new swerch(new shoodtest_01()、 "thread_"+i).start(); }}}部分的な実行結果:
shood_2run ... thread_2run ... thread_4run ... thread_4run ... thread_3run ... thread_3run ... thread_3run ... thread_3run ... thread_2run ... shood_4run ...
この結果は、予想される結果とは少し異なります(これらのスレッドはここで実行されます)。論理的に言えば、実行方法と同期キーワードは同期効果を生成します。これらのスレッドは、実行方法を次々に実行する必要があります。上記のように、同期されたキーワードをメンバーメソッドに追加した後、実際にはメンバーメソッドへのロックです。特定のポイントは、メンバーメソッドがオブジェクトロックとして配置されているオブジェクト自体を使用することです。ただし、この例では、新しい10スレッドテストオブジェクトがあり、各スレッドは独自のスレッドオブジェクトのオブジェクトロックを保持します。これは、間違いなく同期効果を生成しません。したがって、これらのスレッドを同期する場合、これらのスレッドが保持しているオブジェクトロックは共有され、一意にする必要があります!
現時点では、どのオブジェクトが同期されていますか?ロックするのは、この同期メソッドオブジェクトを呼び出すことです。つまり、ThreadTestオブジェクトが異なるスレッドで同期メソッドを実行すると、相互に排他的になります。同期の効果を実現します。したがって、上記の新しいスレッドを変更します(new threadtest_01()、 "thread_" + i).start();新しいスレッド(threadtest、 "thread_" + i).start();
同期方法の場合、ロックは現在のインスタンスオブジェクトです。
上記の例では、同期された方法を使用しています。同期コードブロックを見てみましょう。
public class threadtest_02拡張スレッド{private string lock;プライベート文字列名; public threadtest_02(string name、string lock){this.name = name; this.lock = lock; } @Override public void run(){synchronized(lock){for(int i = 0; i <3; i ++){system.out.println(name+"run ......"); }}} public static void main(string [] args){string lock = new String( "test"); for(int i = 0; i <5; i ++){new shoodtest_02( "threadtest_"+i、lock).start(); }}}実行結果:
Streadtest_0 run ... swreattest_0 run ... swreattest_0 run ... swreattest_1 run ... swreattest_1 run ... swreattest_1 run ... swreattest_1 run ... swreattest_4 run ... swreattest_44 run ... run threadtest_3 run threadtest_3 run ... run ... run ... runtest_2 run ... runtest_2ラン
メインメソッドでは、文字列オブジェクトロックを作成し、このオブジェクトを各ThreadTest2スレッドオブジェクトのプライベート変数ロックに割り当てます。 Javaにはストリングプールがあることがわかっているため、これらのスレッドのロックプライベート変数は、実際にはヒープメモリ内の同じ領域、つまりメイン関数のロック変数が保存されている領域を指しているため、オブジェクトロックが一意で共有されます。スレッドの同期! !
ここで同期したロックされたロック文字列オブジェクト。
同期メソッドブロックの場合、ロックは同期された括弧で構成されたオブジェクトです。
public class threadtest_03拡張スレッド{public synchronized static void test(){for(int i = 0; i <3; i ++){system.out.println(thread.currentthread()。getName()+"run ......"); }} @Override public void run(){test(); } public static void main(string [] args){for(int i = 0; i <5; i ++){new shoodtest_03()。start(); }}}実行結果:
スレッド-0ラン...スレッド0ラン...スレッド0ラン...スレッド4ラン...スレッド4ラン...スレッド4ラン...スレッド1ラン...スレッド1ラン...スレッド1ラン...スレッド2ラン...スレッド2ラン...スレッド2ラン...スレッド3ラン...スレッド3ラン...スレッド-3ラン...
この例では、実行方法は同期法と静的同期方法を使用します。では、ここでの同期ロックは何ですか?静的はオブジェクトを超えており、クラスレベルにあることを知っています。したがって、オブジェクトロックは、静的リリースが配置されているクラスのクラスインスタンスです。 JVMでは、すべてのロードされたクラスには一意のクラスオブジェクトがあり、このインスタンスでは唯一のthreadtest_03.クラスオブジェクトがあります。クラスを作成したインスタンスの数に関係なく、そのクラスインスタンスはまだ1つです!したがって、オブジェクトロックは一意で共有されています。スレッドの同期! !
静的同期方法の場合、ロックは現在のオブジェクトのクラスオブジェクトです。
クラスが同期された静的関数Aと同期インスタンス関数Bを定義する場合、ロックが異なるため、複数のスレッドで2つのメソッドAとBにアクセスする場合、このクラスの同じオブジェクトOBJは同期を構成しません。方法AのロックはオブジェクトOBJであり、BのロックはOBJが属するクラスです。
アップグレードをロックします
Javaには4つの状態があります。ロックフリー状態、偏ったロック状態、軽量ロック状態、ヘビー級ロック状態があり、競争に徐々にエスカレートします。ロックをアップグレードできますが、ダウングレードすることはできません。つまり、軽量ロックにアップグレードした後、バイアスロックをバイアスロックにダウングレードできません。ロックアップグレードのこの戦略ですが、ダウングレードすることはできません。ロックの取得と解放の効率を改善することです。以下の主な部分は、ブログの概要です。CONCURRENCY(II)Java SE1.6で同期しました。
ロックスピン
スレッドが同期方法/コードブロックに入ると、同期方法/コードブロックが他の人が占有していることがわかった場合、待機してブロッキング状態に入ります。このプロセスのパフォーマンスは低いです。
ロックの競争に遭遇したり、物事を待ったりすると、スレッドはブロッキング状態に入ることを心配することができませんが、待機してロックがすぐに解放されるかどうかを確認してください。これはロックスピンです。ある程度、ロックスピンはスレッドを最適化できます。
ポジティブロック
正のロックは、主に競争なしでロックのパフォーマンスの問題を解決するために使用されます。ほとんどの場合、Lock Locksはマルチスレッド競合を持たないだけでなく、常に同じスレッドで複数回取得されます。スレッドを低コストでロックを取得させるために、偏ったロックが導入されます。スレッドがロックを取得すると、スレッドはオブジェクトを複数回ロックできますが、そのような操作が実行されるたびに、CAS(CPUの比較とスワップ命令)操作によるオーバーヘッド消費があります。このオーバーヘッドを減らすために、ロックはそれを取得する最初のスレッドになる傾向があります。次の実行プロセス中にロックが他のスレッドによって取得されない場合、バイアスロックを保持するスレッドを再度同期する必要はありません。
他のスレッドがバイアスロックのために競争しようとしているとき、バイアスロックを保持するスレッドがロックを解放します。
ロック拡張
粒度が小さすぎるロックへの複数または複数の呼び出しは、大きな粒度ロックを備えたロックを呼び出すほど効率的ではありません。
軽量ロック
プログラムの同期パフォーマンスを改善するための軽量ロックの基礎は、「ほとんどのロックでは、同期サイクル全体に競争がない」ということです。これは経験的データです。軽量ロックは、現在のポインティングとロックオブジェクトの状態を保存するために使用される現在のスレッドのスタックフレームにロックレコードと呼ばれるスペースを作成します。競合がない場合、軽量ロックはCAS操作を使用してミューテックスの使用のオーバーヘッドを回避しますが、ロック競合がある場合、ミューテックスのオーバーヘッドに加えて、CASの動作がさらに発生するため、競争の場合、軽量ロックは従来のヘビー級ロックよりも遅くなります。
ロックの公平性
公平性の反対は飢えです。では、「空腹」とは何ですか?他のスレッドが常にCPUを占有しているためにスレッドがCPUの実行時間を取得できない場合、スレッドを「飢えて死ぬ」と呼びます。飢hungの解決策は「公平性」と呼ばれます。すべてのスレッドは、CPUランニングの機会を公平に得ることができます。
スレッドの飢erにはいくつかの主な理由があります:
高優先度のスレッドは、すべての低優先度スレッドのCPU時間を消費します。 1〜10のスレッドごとに優先度を個別に設定できます。優先度のスレッドが高いほど、CPUを取得するのに時間がかかります。ほとんどのアプリケーションでは、優先順位を変更しないことが最善です。
スレッドは、同期ブロックに入るのを待っている状態で永続的にブロックされています。 Javaの同期コード領域は、スレッドの飢erを引き起こす重要な要素です。 Javaの同期コードブロックは、入力するスレッドの順序を保証するものではありません。これは、理論的には、同期コード領域を入力しようとするときに常にブロックされる1つ以上のスレッドがあることを意味します。なぜなら、他のスレッドは常にアクセスを得るためにそれらよりも優れており、CPUランニングの機会を得られず、「飢えて死ぬ」ことを意味します。
スレッドは、それ自体が永久に完了を待っているオブジェクトを待っています。複数のスレッドがwait()メソッドの実行中にあり、その上でnotify()を呼び出しても、スレッドが目覚めることを保証しない場合、すべてのスレッドは連続的な待機状態にある可能性があります。したがって、他の待機スレッドを常に目覚めることができるため、1つの待機スレッドが目覚めないリスクがあります。
スレッド「空腹」の問題を解決するために、ロックを使用して公平性を達成できます。
ロックの繰り返し可能性
スレッドが別のスレッドでロックを保持するオブジェクトを要求すると、スレッドがブロックされることがわかりますが、スレッドがロックを単独で保持するオブジェクトを要求すると成功することができますか?答えは、成功が成功する可能性があり、成功の保証はスレッドロックの「再突入」であるということです。
「復活可能」とは、ブロックせずに独自の内部ロックを再度入手できることを意味します。次のように:
パブリッククラスの父{public synchronized void method(){// do something}} public class childは父親を拡張します{public synchronized void method(){//何かsuper.method(); }}
リエントラントでない場合、上記のコードはデッドロックされます。これは、ChildのMethod()を呼び出すことで、最初に親クラスの父親の組み込みロックを取得し、次に子供の組み込みロックを取得するためです。親クラスのメソッドを呼び出すときは、親クラスの組み込みロックに再び戻る必要があります。リエントラントでない場合は、デッドロックに陥る可能性があります。
Javaマルチスレッドの再経路性の実装は、各ロックでそれを占有するリクエスト計算とスレッドを関連付けることです。カウントが0の場合、ロックが占有されていないと考えられており、任意のスレッドはロックの所有権を取得できます。スレッドが正常にリクエストすると、JVMはロックを保持しているスレッドを記録し、カウントを1に設定します。他のスレッドがロックを要求する場合、待機する必要があります。スレッドが再びロックを取得するように要求すると、カウントは+1になります。占有スレッドが同期コードブロックを終了すると、カウントは0になるまで-1になります。ロックはリリースされます。そうして初めて、他のスレッドにはロックの所有権を獲得する機会があります。
ロックとその実装クラス
java.util.concurrent.locksは、非常に柔軟なロックメカニズムを提供し、ロックおよび待機条件のためのフレームワークのインターフェイスとクラスを提供します。組み込みの同期やモニターとは異なり、ロックと条件を使用する柔軟性を高めることができます。そのクラス構造図は次のとおりです。
REENTRANTLOCK:REENTRANT MUTEX LOCK、ロックインターフェイスの主な実装。
Reentrantreadwritelock:
readwritelock:readwriteLockは、1つは読み取り専用操作用、もう1つは書き込み操作用のロックを維持しています。
セマフォ:カウントセマフォ。
条件:ロックの目的は、スレッドがロックを取得し、特定の状態が待機しているかどうかを確認できるようにすることです。
CyclicBarrier:スレッドのグループが共通の障壁に達するまで互いに待つことを可能にする同期補助クラス。