『Java同時プログラミング』の7:活発性の危険を避ける

5734 ワード

詳細
すべてのスレッドが固定順序でロックを取得すると、プログラムでロック順序のデッドロックの問題は発生しません.
ロック順序によってデッドロックを回避するには:
public class InduceLockOrder {
    private static final Object tieLock = new Object();

    public void transferMoney(final Account fromAcct,
                              final Account toAcct,
                              final DollarAmount amount)
            throws InsufficientFundsException {
        class Helper {
            public void transfer() throws InsufficientFundsException {
                if (fromAcct.getBalance().compareTo(amount) < 0)
                    throw new InsufficientFundsException();
                else {
                    fromAcct.debit(amount);
                    toAcct.credit(amount);
                }
            }
        }
        int fromHash = System.identityHashCode(fromAcct);
        int toHash = System.identityHashCode(toAcct);

        if (fromHash < toHash) {
            synchronized (fromAcct) {
                synchronized (toAcct) {
                    new Helper().transfer();
                }
            }
        } else if (fromHash > toHash) {
            synchronized (toAcct) {
                synchronized (fromAcct) {
                    new Helper().transfer();
                }
            }
        } else {
            synchronized (tieLock) {
                synchronized (fromAcct) {
                    synchronized (toAcct) {
                        new Helper().transfer();
                    }
                }
            }
        }
    }

    interface DollarAmount extends Comparable {
    }

    interface Account {
        void debit(DollarAmount d);

        void credit(DollarAmount d);

        DollarAmount getBalance();

        int getAcctNo();
    }

    class InsufficientFundsException extends Exception {
    }
}

 
10.1.3コラボレーションオブジェクト間で発生したデッドロック
ロックがあるときに外部メソッドを呼び出すと、アクティブな問題が発生します.この外部メソッドでは、他のロックが取得されるか、ブロック時間が長すぎるため、他のスレッドが現在保持されているロックをタイムリーに取得できない可能性があります.
あるメソッドを呼び出すときにロックを持つ必要がない場合、この呼び出しはオープン呼び出しOpen Callと呼ばれ、オープン呼び出しに依存するクラスは通常、より良い動作を示し、書きやすくなります.
class CooperatingNoDeadlock {
    @ThreadSafe
    class Taxi {
        @GuardedBy("this")
        private Point location, destination;
        private final Dispatcher dispatcher;

        public Taxi(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }

        public synchronized Point getLocation() {
            return location;
        }

        public synchronized void setLocation(Point location) {
            boolean reachedDestination;
            synchronized (this) {
                this.location = location;
                reachedDestination = location.equals(destination);
            }
            if (reachedDestination)
                dispatcher.notifyAvailable(this);
        }

        public synchronized Point getDestination() {
            return destination;
        }

        public synchronized void setDestination(Point destination) {
            this.destination = destination;
        }
    }

    @ThreadSafe
    class Dispatcher {
        @GuardedBy("this")
        private final Set taxis;
        @GuardedBy("this")
        private final Set availableTaxis;

        public Dispatcher() {
            taxis = new HashSet();
            availableTaxis = new HashSet();
        }

        public synchronized void notifyAvailable(Taxi taxi) {
            availableTaxis.add(taxi);
        }

        public Image getImage() {
            Set copy;
            synchronized (this) {
                copy = new HashSet(taxis);
            }
            Image image = new Image();
            for (Taxi t : copy)
                image.drawMarker(t.getLocation());
            return image;
        }
    }

    class Image {
        public void drawMarker(Point p) {
        }
    }

}

プログラムではできるだけオープンコールを使用します.ロックを持つときに外部メソッドプログラムを呼び出すよりも、オープン呼び出しに依存するプログラムのデッドロック解析が容易である.
 
10.2デッドロックの回避と診断
オープンコールをできるだけ使用すると、デッドロック分析プロセスを大幅に簡素化できます.
タイミングをサポートするロック:内蔵ロックメカニズムの代わりにロッククラスのタイミングtryLock機能を使用することを表示します.このテクノロジーは、ネストされたメソッド呼び出しで複数のロックを要求した場合、2つのロックを同時に取得する場合にのみ有効です.
 
10.3その他の活動性の問題
飢餓:
スレッドが必要なリソースにアクセスできずに実行を続行できない場合、飢餓starvationが発生します.最も一般的なリソースはCUPクロックサイクルです.たとえば、スレッドの優先度が適切でない場合に発生します.
Javaを使用するスレッドの優先度を避けるには、プラットフォーム依存性が増加し、アクティブ性の問題を引き起こす可能性があります.
 
悪い応答性:
1つのスレッドが長い間ロックを占有している場合、他のコンテナにアクセスするスレッドは長い間待たなければなりません.
 
ロック:
Liflockは、トランザクション・メッセージを処理するアプリケーションでよく発生します.メッセージが正常に処理されない場合、メッセージ処理メカニズムはトランザクション全体をロールバックし、キューの先頭に再配置し、繰り返し実行し、先頭に配置されます.
再試行メカニズムにランダム性を導入することによって.活錠の発生を効果的に回避できます.
 
小結:アクティブな障害が発生した場合、アプリケーションを終了する以外にこの障害から回復するメカニズムがないため、アクティブな障害が発生した場合、非常に深刻な問題です.最も一般的なアクティブな障害は、ロック順序のデッドロックです.設計時には、ロック順序のデッドロックを回避し、スレッドが複数のロックを取得する際に一貫した順序を採用することを確保するとともに、ネストされた呼び出しロックを防止するために、オープンコールを使用することが望ましい.
 
私のブログはすでに引っ越して、新しい住所は:http://yidao620c.github.io/