JAvaでの同期の過剰使用を避ける
同期の使用が多すぎると、同期の使用が多すぎると、パフォーマンスが低下したり、デッドロックしたり、不確定な動作をしたりする可能性があります.デッドロックの危険を回避するために、同期された方法またはコードブロックでは、お客様の制御を放棄しないでください.同期された領域の内部では、書き換えられない公有または保護されたメソッドを呼び出さないでください.このようなメソッドは、同期領域を含むクラスの観点から外来者です.この外来メソッドは、このクラスにコールバックする別のスレッドを作成するインプリメンテーションを提供することができる.その後、新しいスレッドが元のスレッドが所有するロックを取得しようとすると、新しいスレッドがブロックされ、スレッドを作成する方法がこのスレッドがタスクを完了するのを待っている場合、デッドロックが形成されます.異なる領域以外で呼び出される外来メソッドはオープン呼び出しと呼ばれ、デッドロックを回避できるほか、オープン呼び出しは同時性を極めて高めることができ、外来メソッドの実行時間は任意に長くなる可能性がある.例えば、同期領域内で外来メソッドを呼び出すと、外来メソッドの実行期間中に、他のスレッドが共有オブジェクトにアクセスするには、不要な拒否があります.通常、同期領域ではできるだけ少ない仕事をしなければなりません.ロックを取得し、共有データをチェックし、必要に応じてデータを変換し、ロックを解除します.少ない時間の動作を実行する必要がある場合は、この方法を同期領域の外部に移動する方法を考えなければなりません.クラスまたは静的メソッドが可変静的ドメインから使用されている場合、共有インスタンスとは異なる単一スレッドでのみ使用されることが多い場合でも、内部で同期する必要があります.この場合、他の顧客が外部同期を実行することを保証することは不可能であるため、顧客が外部同期を実行することは不可能である.簡単に言えば、デッドロックやデータ破壊を避けるために、同期領域内部から外来メソッドを呼び出さないでください.ループの外でwait Objectを呼び出さないでください.waitメソッドの役割は、スレッドを使用して条件を待つことです.彼は必ず同期領域で呼び出され、その同期領域は呼び出されたオブジェクトをロックします.waitの方法の標準モード:
synchronized(obj){while(は、waitループモードを使用してwaitメソッドを呼び出すことが多い.ループの外でwaitを呼び出さないでください.ループは待機する前後のテスト条件に使用されます.待機する前に条件をテストし、条件が成立したら待機をスキップし、柔軟性を確保します.条件が成立し、スレッドが待機する前にnotify(またはnotifyAll)メソッドが呼び出された場合、スレッドが常に待機から覚める保証はありません.
package com.etrip.effective; import java.util.LinkedList; import java.util.List; /** * @author longgangbai * @date 2012-11-22 * */ public abstract class WorkQueue { private final List queue=new LinkedList(); private boolean stopped=false; protected WorkQueue(){ new WorkerThread().start(); } public final void enqueue(Object workItem){ synchronized (queue) { queue.add(workItem); queue.notify(); } } public final void stop(){ synchronized (queue) { stopped=true; queue.notify(); } } public abstract void processItem(Object workItem)throws InterruptedException; private class WorkerThread extends Thread{ @Override public void run(){ while(true){ Object workItem=null; synchronized (queue) { while(queue.isEmpty() && !stopped){ try { queue.wait(); } catch (InterruptedException e) { return; } } if(stopped){ return ; } workItem=queue.remove(0); try { processItem(workItem); } catch (InterruptedException e) { return; } } } } } }
ワークキューを表示するDisplayWorkQueueを作成します.package com.etrip.effective; /** * * @author longgangbai */ public class DisplayWorkQueue extends WorkQueue { public void processItem(Object workItem)throws InterruptedException { System.out.println(workItem); Thread.sleep(1000); } }
デッドロックワークキュークラスを作成するには、次の手順に従います.package com.etrip.effective; /** * * @author longgangbai */ public class DeadLockQueue extends WorkQueue { public void processItem(final Object workItem)throws InterruptedException { Thread child=new Thread(){ public void run(){ enqueue(workItem); } }; child.start(); child.join();//dead lock } }
両方のスレッドでオブジェクトロックを取得する必要があります.デッドロックの解決策は、workqueueの同期方法を変更することです.
変更後は次のようになります.abstract class WorkQueueExt{ private final List queue=new LinkedList(); private boolean stopped=false; protected WorkQueueExt(){ new WorkerThread().start(); } public final void enqueue(Object workItem){ synchronized (queue) { queue.add(workItem); queue.notify(); } } public final void stop(){ synchronized (queue) { stopped=true; queue.notify(); } } public abstract void processItem(Object workItem)throws InterruptedException; private class WorkerThread extends Thread{ @Override public void run(){ while(true){ Object workItem=null; synchronized (queue) { while(queue.isEmpty() && !stopped){ try { queue.wait(); } catch (InterruptedException e) { return; } } if(stopped){ return ; } workItem=queue.remove(0); } // , try { processItem(workItem); } catch (InterruptedException e) { return; } } } } }
待機状態にあるすべてのスレッドが同じ条件を待機し、この条件から起動できるスレッドが1つしかない場合は、notifyAllではなくnotifyを呼び出すことを選択できます.notifyAllを使用してnotifyより優先する推奨事項には、notifyAllを使用しても正確性には影響しませんが、パフォーマンスに影響します.実際には、システムの観点から、待機スレッド数の線形劣化から平方レベルに至るまで、いくつかのデータ構造のパフォーマンスが低下します.