Javaマルチスレッドの同期の学習

6869 ワード

Javaベースが弱い場合や、javaマルチスレッドをよく知らない場合は、まずこの記事「Javaマルチスレッドのスレッド定義、ステータス、プロパティの学習」を参照してください.
同期はjavaマルチスレッドの難点であり、android開発時にもあまり応用されていませんが、同期に慣れていない理由ではありません.この文章がjavaの同期をより多くの人に理解して適用できることを望んでいます.マルチスレッドの応用では、2つ以上のスレッドが同じデータへのアクセスを共有する必要がある.2つのスレッドが同じオブジェクトにアクセスし、各スレッドがオブジェクトを変更する方法を呼び出した場合、この場合、通常、競合条件になります.競争条件が最も理解しやすい例は、例えば汽車が切符を売って、汽車の切符は一定ですが、汽車の切符を売る窓口はあちこちにあり、各窓口は1つのスレッドに相当し、こんなに多くのスレッドはすべての汽車の切符という資源を共用しています.そしてその原子性を保証することはできず、1つの時点で2つのスレッドが同時にこのリソースを使用すると、彼らが取り出した汽車の切符は同じ(座席番号は同じ)ので、乗客に迷惑をかけます.解決策は、あるスレッドが汽車の切符という資源を使うとき、私たちはそれに鍵を渡して、仕事が終わったら、別のこの資源を使うスレッドに鍵をかけます.これにより、上記のような状況は発生しません.
1.ロックオブジェクトsynchronizedキーワードは自動的にロックと関連条件を提供し、多くの場合、明示的なロックが必要な場合はsynchronizedを使用するのが便利ですが、ReentrantLockクラスと条件オブジェクトを理解すると、synchronizedキーワードをよりよく理解することができます.ReentrantLockはJAVA SE 5.0に導入され、ReentrantLockでコードブロックを保護する構成は以下の通りである.

mLock.lock();
try{
...
}
finally{
mLock.unlock();
}


この構造は、任意の時点で1つのスレッドだけが臨界領域に入ることを保証し、1つのスレッドがロックオブジェクトを封鎖すると、他のスレッドはlock文を通過できません.他のスレッドがlockを呼び出すと、最初のスレッドがロックオブジェクトを解放するまでブロックされます.ロック解除の操作をfinallyに置く必要があります.臨界領域で異常が発生した場合、ロックは解放されなければなりません.そうしないと、他のスレッドは永遠にブロックされます.
2.条件オブジェクトが臨界領域に入ると、ある条件が満たされた後に実行されることが分かった.ロックが取得されたが、役に立つ作業ができないスレッドを管理するために条件オブジェクトを使用するには、条件オブジェクトを条件変数とも呼びます.次の例を見て、条件オブジェクトが必要な理由を見てみましょう.
銀行で振り替える必要があるシーンを仮定して、まず銀行のクラスを書きました.その構造関数は口座の数と口座の金額を入力する必要があります.

public class Bank {
private double[] accounts;
  private Lock bankLock;
  public Bank(int n,double initialBalance){
    accounts=new double[n];
    bankLock=new ReentrantLock();
    for (int i=0;i 
 

次に私达は金を引き出して、1つの金を引き出す方法を书いて、fromは振り替え方で、toは受信者で、amount振り替え金额、结果は私达は振り替え方の残高が不足することを発见して、もし他のスレッドがこの振り替え方に更に十分なお金を贮めるならば振り替えに成功することができて、しかしこのスレッドはすでにロックを获得して、それは排他性を持って、他のスレッドもロックを获得して预金の操作を行うことができませんこれが条件オブジェクトを導入する必要がある理由です.

  public void transfer(int from,int to,int amount){
    bankLock.lock();
    try{
      while (accounts[from] 
 

1つのロックオブジェクトには複数の関連条件オブジェクトがあり、newConditionメソッドで条件オブジェクトを取得できます.条件オブジェクトを取得した後、awaitメソッドを呼び出すと、現在のスレッドがブロックされ、ロックが放棄されます.

public class Bank {
private double[] accounts;
  private Lock bankLock;
  private Condition condition;
  public Bank(int n,double initialBalance){
    accounts=new double[n];
    bankLock=new ReentrantLock();
    //      
    condition=bankLock.newCondition();
    for (int i=0;i 
 

ロックを取得するスレッドとawaitメソッドを呼び出すスレッドは本質的に異なり、1つのスレッドが呼び出すawaitメソッドとなると、条件の待機セットに入ります.ロックが使用可能である場合、スレッドはすぐにロックを解除することはできません.逆に、別のスレッドが同じ条件のsignalAllメソッドを呼び出すまでブロックされています.別のスレッドが私たちの前の振り替え先に振り替える準備をしている場合はconditionを呼び出すだけです.signalAll();呼び出しは、この条件のために待機しているすべてのスレッドを再アクティブにします.1つのスレッドがawaitメソッドを呼び出すと、彼は自分を再アクティブにすることができず、signalAllメソッドを呼び出して自分をアクティブにすることを望んでいます.待機しているスレッドをアクティブにする他のスレッドがなければ、デッドロック現象が発生し、他のすべてのスレッドがブロックされている場合、最後のアクティブスレッドは他のスレッドのブロック状態を解除する前にawaitを呼び出します.これもブロックされ、他のスレッドのブロックを解除できるスレッドがなく、プログラムが停止されます.では、signalAllはいつ呼び出されますか?通常、signalAllは、スレッドの方向が変化するのを待つのに役立つはずです.この例では、1つの口座残高が変化した場合、待機しているスレッドは残高をチェックする機会があるはずです.

 public void transfer(int from,int to,int amount) throws InterruptedException {
    bankLock.lock();
    try{
      while (accounts[from] 
 

signalAllメソッドを呼び出すと、すぐに待機スレッドがアクティブになるわけではありません.待機スレッドのブロックを解除するだけで、現在のスレッドが同期メソッドを終了した後、競合によってオブジェクトへのアクセスを実現できます.もう1つの方法はsignalであり、あるスレッドのブロックをランダムに解除し、スレッドがまだ実行できない場合は再びブロックされ、他のスレッドがsignalを再び呼び出さなければシステムはデッドロックする.
3.SynchronizedキーワードLockとConditionインタフェースは、プログラム設計者に高度なロック制御を提供しているが、多くの場合、そのような制御は必要なく、java言語の内部に埋め込まれたメカニズムを使用することができる.Java 1から0版からJavaの各オブジェクトには内部ロックがあります.メソッドがsynchronizedキーワードで宣言されると、オブジェクトのロックはメソッド全体を保護します.すなわち、このメソッドを呼び出すには、スレッドが内部のオブジェクトロックを取得する必要があります.言い換えれば、

public synchronized void method(){

}

に等しい

public void method(){
this.lock.lock();
try{

}finally{
this.lock.unlock();
}

上記の銀行の例では、Bankクラスのtransferメソッドをsynchronizedとして宣言することができ、表示されたロックを使用するのではなく、synchronizedとして宣言することができます.内部オブジェクトロックには1つの関連条件しかなく、wait増幅は1つのスレッドを待機セットに追加し、notifyAllまたはnotifyメソッドは待機スレッドのブロック状態を解除します.すなわちwaitはconditionを呼び出すことに相当する.await()、notifyAllはconditionに等しい.signalAll();
私たちの上の例transferの方法もこのように書くことができます.

  public synchronized void transfer(int from,int to,int amount)throws InterruptedException{
    while (accounts[from] 
 

synchronizedキーワードを使用してコードを記述するのは簡潔であることがわかります.もちろん、このコードを理解するには、各オブジェクトに内部ロックがあり、そのロックに内部条件があることを理解する必要があります.synchronizedメソッドにアクセスしようとするスレッドはロックによって管理され、waitを呼び出すスレッドは条件によって管理されます.
4.同期ブロック上記で述べたように、Javaオブジェクトごとにロックがあり、スレッドは同期方法を呼び出してロックを得ることができ、また別のメカニズムはロックを得ることができ、同期ブロックに入ることによって、スレッドが次の形式のブロックに入る.

synchronized(obj){

}

そこで彼はobjの鍵を手に入れた.Bank類を見てみましょう

public class Bank {
private double[] accounts;
private Object lock=new Object();
  public Bank(int n,double initialBalance){
    accounts=new double[n];
    for (int i=0;i 
 

ここでlockオブジェクト作成は、Javaオブジェクトごとに持つロックのみを使用します.開発者がクライアントロックと呼ばれる追加の原子操作を実現するためにオブジェクトのロックを使用する場合があります.たとえばVectorクラスでは、そのメソッドは同期されます.次に、Vectorに銀行残高を格納すると仮定します.

 public void transfer(Vectoraccounts,int from,int to,int amount){
 accounts.set(from,accounts.get(from)-amount);
 accounts.set(to,accounts.get(to)+amount;
}


Vecrorクラスのgetメソッドとsetメソッドは同期されていますが、これは私たちには役に立ちません.最初のget呼び出しが完了すると、1つのスレッドがtransferメソッドで実行権を剥奪される可能性があり、別のスレッドが同じ記憶位置に異なる値を格納する可能性がありますが、このロックをキャプチャすることができます.

 public void transfer(Vectoraccounts,int from,int to,int amount){
 synchronized(accounts){
 accounts.set(from,accounts.get(from)-amount);
 accounts.set(to,accounts.get(to)+amount;
 }
}


クライアントロック(同期コードブロック)は非常に脆弱であり、通常は推奨されず、一般的に同期を実現するにはjavaが望ましい.util.concurrentパッケージの下に提供されるクラス、例えばブロックキュー.同期方法があなたのプログラムに適している場合は、できるだけ同期方法を使用してください.彼はコードの作成数を減らし、エラーの確率を減らすことができます.特にロック/condition構造が提供する独自の特性を使用する必要がある場合は、ロック/conditionを使用します.
以上が本文のすべての内容で、みんなの学習に役立つことを望みます.