Java Foundationが弱い場合、またはJava MultiThreadingを十分に理解していない場合は、この記事「Java MultiThreadingのスレッド定義、状態、およびプロパティをご覧ください」をご覧ください。
同期は、Java Multi-Threadingにとって常に困難なポイントであり、Android開発を行っている場合はめったに使用されませんが、これが同期に精通していない理由ではありません。この記事が、より多くの人々がJava同期を理解し、適用できるようになることを願っています。
マルチスレッドアプリケーションでは、2つ以上のスレッドが同じデータへのアクセスを共有する必要があります。これは通常、2つのスレッドが同じオブジェクトにアクセスし、各スレッドがオブジェクトを変更するメソッドを呼び出す場合、人種条件になります。
競争条件の最も簡単な例は次のとおりです。たとえば、列車のチケットは確実ですが、どこにでも列車のチケットを販売するための窓があり、各ウィンドウは1つのスレッドに相当し、多くのスレッドがすべての列車のチケットリソースを共有しています。そして、それはその原子性を保証することはできません。 2つのスレッドが時点でこのリソースを使用する場合、彼らが取り出す列車のチケットは同じです(座席番号は同じです)、これは乗客に問題を引き起こします。解決策は、スレッドが列車のチケットリソースを使用したい場合、ロックを提供し、作業を終了した後、このリソースを使用したい別のスレッドにロックを与えることです。これにより、上記の状況は発生しません。
1.オブジェクトをロックします
同期されたキーワードは、ロックと関連条件を自動的に提供します。明示的なロックが必要なほとんどの場合に同期して使用するのは非常に便利です。ただし、ReentrantLockクラスと条件付きオブジェクトを理解すると、同期されたキーワードをよりよく理解できます。 ReentrantLockはJava SE 5.0で導入されています。コードブロックの構造は、次のようにReentrantLockを使用して保護されています。
mlock.lock(); try {...}最後に{mlock.unlock();}この構造により、1つのスレッドのみがいつでもクリティカルエリアに入ることが保証されます。スレッドがロックオブジェクトをブロックすると、ロックステートメントを渡すことはできません。他のスレッドがロックを呼び出すと、最初のスレッドがロックオブジェクトを解放するまでブロックされます。ロック解除操作を最終的に配置することが非常に必要です。重要な領域で例外が発生した場合、ロックをリリースする必要があります。そうしないと、他のスレッドが永久にブロックされます。
2。条件付きオブジェクト<br />重要な領域に入ると、特定の条件が満たされた後にのみ実行できることがわかります。条件付きオブジェクトを使用して、ロックを取得したが有用な作業を行うことができないスレッドを管理します。条件付きオブジェクトは、条件変数とも呼ばれます。
次の例を見て、条件付きオブジェクトが必要な理由を確認しましょう
シナリオでは、銀行の転送を使用する必要があると仮定します。最初に銀行クラスを作成し、そのコンストラクターをアカウントの数と口座の量に転送する必要があります。
パブリッククラスバンク{プライベートダブル[]アカウント;プライベートロックバンクロック。パブリックバンク(int n、double initialbalance){accounts = new double [n]; banklock = new ReentrantLock(); for(int i = 0; i <accounts.length; i ++){accounts [i] = initialbalance; }}}次に、お金を引き出して撤退方法を書きたいと思います。からの移転者は、受信者であり、金額の転送量です。その結果、譲渡人の残高が不十分であることがわかりました。他のスレッドが譲渡人に十分なお金を節約すれば、譲渡は成功する可能性があります。ただし、このスレッドはロックを取得しました。これは排他的であり、他のスレッドは堆積操作を実行するためにロックを取得できません。これが、条件付きオブジェクトを導入する必要がある理由です。
public void transfer(int、int to、int on){banklock.lock(); try {while(accounts [from] <lument){// wait}}最後に{banklock.unlock(); }}ロックオブジェクトには、複数の関連条件オブジェクトがあります。 NewConditionメソッドを使用して、条件オブジェクトを取得できます。条件オブジェクトを取得した後、待機方法を呼び出し、現在のスレッドがブロックされ、ロックが放棄されます。
パブリッククラスバンク{プライベートダブル[]アカウント;プライベートロックバンクロック。プライベート状態の状態;パブリックバンク(int n、double initialbalance){accounts = new double [n]; banklock = new ReentrantLock(); //条件オブジェクトを取得= banklock.newcondition(); for(int i = 0; i <accounts.length; i ++){accounts [i] = initialbalance; }} public void Transfer(int、int、int、int fort)throws arturtedexception {banklock.lock(); try {while(accounts [from] <lument){//現在のスレッドをブロックして、lock condition.await(); }}最後に{banklock.unlock(); }}}ロックを待っているスレッドは、waintメソッドを呼び出すスレッドとは本質的に異なります。スレッドが待機方法を呼び出すと、その条件の待機セットに入ります。ロックが利用可能になった場合、スレッドはすぐにロック解除できませんが、代わりに別のスレッドが同じ条件でSignalallメソッドを呼び出すまでブロック状態になります。別のスレッドが以前の転送者に送金する準備ができたら、condition.signalall()を呼び出すだけです。この呼び出しは、この状態を待っているすべてのスレッドを再アクティブ化します。
スレッドが待機方法を呼び出すと、それ自体を再アクティブ化することはできず、他のスレッドがSignalallメソッドを呼び出してそれ自体をアクティブにすることを望んでいます。他のスレッドが待っているスレッドをアクティブにしない場合、デッドロックが発生します。他のすべてのスレッドがブロックされ、最後のアクティブスレッドコールが他のスレッドのブロックを解除する前に待っている場合、ブロックされます。スレッドは他のスレッドのブロックを解除できず、プログラムは停止されます。
次に、Signalallはいつ呼び出されますか?通常、スレッドの方向が変更されるのを待つときにSignalallを呼び出すことは有益です。この例では、アカウントのバランスが変更された場合、待機スレッドはバランスを確認する機会があるはずです。
public void transfer(int、int、int、int boment)は邪魔をします{banklock.lock(); try {while(accounts [from] <lument){//現在のスレッドをブロックして、lock condition.await(); } //転送操作... condition.signalall(); }最後に{banklock.unlock(); }}Signalallメソッドが呼び出されると、待機スレッドはすぐにアクティブになりません。現在のスレッドが同期メソッドを終了した後に競合することにより、これらのスレッドがオブジェクトへのアクセスを実現できるように、待機スレッドのブロックを解除するだけです。別の方法は、スレッドのランダムにブロックを解除する信号です。スレッドがまだ実行できない場合、再びブロックされます。他のスレッドが再度呼び出されない場合、システムはデッドロックされます。
3。同期されたキーワード
ロックと条件のインターフェイスは、プログラマーに高度なロック制御を提供しますが、ほとんどの場合、そのような制御は必要ありません。Java言語に埋め込まれたメカニズムを使用できます。 Javaバージョン1.0から始めて、Javaのすべてのオブジェクトには内部ロックがあります。メソッドが同期されたキーワードで宣言されている場合、オブジェクトのロックはメソッド全体を保護します。つまり、この方法を呼び出すには、スレッドは内部オブジェクトロックを取得する必要があります。
言い換えると、
public synchronized void method(){}に相当
public void method(){this.lock.lock(); try {}最後に{this.lock.unlock();}上記の銀行の例では、表示されたロックを使用する代わりに、銀行クラスの転送方法を同期していると宣言できます。
内部オブジェクトロックには、関連する条件は1つだけです。待機拡大がスレッドに待機セットに追加されます。通知または通知メソッドが待機スレッドのブロックを解除します。言い換えれば、待機はcondition.await()を呼び出すのと同等です。notifyallはcondition.signalall()に相当します。
上記の例の転送方法は、次のように書くこともできます。
public同期void転送(int、int、int not)throws arturtedexception {while(accounts [from] <ant){wait(); } //転送操作... notifyall(); }同期されたキーワードを使用してコードを書き込むことがはるかに簡単であることがわかります。もちろん、このコードを理解するには、各オブジェクトに内部ロックがあり、ロックに内部条件があることを理解する必要があります。ロックは、同期された方法を入力しようとするスレッドを管理し、条件は待機と呼ばれるスレッドを管理します。
4。同期ブロッキング<br />上記のすべてのJavaオブジェクトにはロックがあり、スレッドは同期方法を呼び出してロックを取得できると述べ、ロックを取得する別のメカニズムがあります。同期ブロックを入力することにより、スレッドが次の形式のブロックに入るとき:
同期(obj){}それで彼はOBJのロックを手に入れました。銀行のクラスを見てみましょう
パブリッククラスバンク{private double [] accounts; private object lock = new object();パブリックバンク(int n、double initialbalance){accounts = new double [n]; for(int i = 0; i <accounts.length; i ++){accounts [i] = initialbalance; }} public void transfer(int、int、int on){synchronized(lock){//転送操作...}}}}ここでは、ロックオブジェクトの作成は、各Javaオブジェクトが保持しているロックを使用するために単に使用されます。開発者は、オブジェクトのロックを使用して、クライアントロックと呼ばれる追加の原子操作を実装することがあります。たとえば、ベクトルクラス、その方法は同期します。銀行の残高がベクターに保管されていると仮定します
public void転送(vector <double> accounts、int、from、int、int boment){accounts.set(from、accounts.get(from)-amount); accounts.set(to、accounts.get(to)+rument;}VecrorクラスのGet and Set Methodは同期していますが、これは私たちに役立ちませんでした。取得する最初の呼び出しが完了した後、1つのスレッドが転送方法で実行する権利を拒否される可能性は完全にあります。そのため、別のスレッドが同じストレージの場所に異なる値を保存している可能性がありますが、このロックを傍受することができます
public void転送(vector <double> accounts、int、from、int to、int on){synchronized(accounts){accounts.set(from、accounts.get(from)-amount); accounts.set(to、accounts.get(to)+rument;}}クライアントロック(同期コードブロック)は非常に壊れやすく、通常は推奨されません。一般的に、blockingキューなど、java.util.concurrentパッケージの下で提供されるクラスを使用することをお勧めします。同期方法がプログラムに適している場合は、同期方法を使用してみてください。書かれたコードの数を減らし、エラーの可能性を減らすことができます。ロック/コンディション構造によって提供される一意の機能を使用する必要がある場合は、ロック/コンディションのみを使用してください。
上記はこの記事に関するものです。すべての人の学習に役立つことを願っています。