0

したがって、次のようなきちんとしたクラスがあります。

class ConnectionObserver {
    private List<DbConnection> connections;

    // singleton logic

    public synchronized void closeConnection(int id) {
        for(Iterator<DbConnection> it = connections.iterator(): it.hasNext()) {
            DbConnection conn = it.next();
            if(conn.getId() == id) {
                conn.close();
                it.remove();
            }
        }
    }

    public int countOpenConnections() {
        int open = 0;
        for(DbConnection conn : connections) {
            if(conn.isOpen()) {
                ++open;
            }
        }
        return open;
    }

    // more synchronized methods which alter the list via iterators
}

問題は、複数のスレッドがシングルトンにアクセスするときに、リストを変更するメソッドを同期した呼び出しと、同期されたメソッドの1つによってリストが変更されたために失敗することがある開いている接続をカウントしようとする呼び出しです。

countOpenConnectionsメソッドを同期させるだけでは問題は解決しないと確信しています。リストを作成しCollections.synchronizedListてもあまり効果はないと思います。

私を助けることができるアプローチを持っている人がいますか?

4

4 に答える 4

1

ファイナルを作成できる場合はList、リスト自体で同期できます。これにより、一度に1つのスレッドのみがリストにモニターを追加できます。この種の鈍い同期は、ロックの競合を増やすという犠牲を払って、当面の問題を解決します。一度にアクセスできるスレッドは1Listつだけです。しかし、なぜ複数の読み取りスレッドがリストに同時にアクセスできないのですか?結局、リストを変更していません...

を入力しますReentrantReadWriteLock。これにより、複数のスレッドが読み取ることができますが、スレッドが書き込みを行っている場合は、すべてを待機する必要があります。「読み取り」と「書き込み」の2つのモードがありました(そのため、この名前が付けられています)。これにより、変更するメソッドと変更しないメソッドを分離でき、Listロックの競合を減らすことができます。

class ConnectionObserver {

    private List<DbConnection> connections;
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void closeConnection(int id) {
        final Lock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            for (Iterator<DbConnection> it = connections.iterator(); it.hasNext();) {
                DbConnection conn = it.next();
                if (conn.getId() == id) {
                    conn.close();
                    it.remove();
                }
            }
        } finally {
            writeLock.unlock();
        }
    }

    public int countOpenConnections() {
        int open = 0;
        final Lock readLock = readWriteLock.readLock();
        readLock.lock();
        try {
            for (DbConnection conn : connections) {
                if (conn.isOpen()) {
                    ++open;
                }
            }
        } finally {
            readLock.unlock();
        }
        return open;
    }
    // more synchronized methods which alter the list via iterators
}

明らかに、にアクセスする他のメソッドを変更して、適切なロックを事前に取得し、キーワードListを削除します。synchronized

別の注意点として、私はaの使用法を理解していません-特定のIDを持つaをList検索する必要があるようです。より良い選択ではないでしょう(線形時間検索ではなく一定...)ListDbConnectionMap

    private Map<Integer, DbConnection> connections;

    public void closeConnection(int id) {
        final Lock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            final DbConnection dbConnection = connections.remove(id);
            if (dbConnection == null) {
                //handle invalid remove attempt
            } else {
                dbConnection.close();
            }
        } finally {
            writeLock.unlock();
        }
    } 

    public int countOpenConnections() {
        int open = 0;
        final Lock readLock = readWriteLock.readLock();
        readLock.lock();
        try {
            for (final DbConnection conn : connections.values()) {
                if (conn.isOpen()) {
                    ++open;
                }
            }
        } finally {
            readLock.unlock();
        }
        return open;
    }
于 2013-03-19T08:34:41.050 に答える
0

接続を閉じるたびに、リストから接続を削除します。したがって、接続のサイズを返すだけです。また、接続オブザーバーとして、接続クローズイベントをリッスンし、開いている接続のみでリストを更新し続けることができます。

于 2013-03-19T08:27:52.387 に答える
0

メソッドcountOpenConnectionsを同期させるだけでは、問題は解決しないと確信しています。

実際には、プログラムを正しく同期させるために必要なのはまさにそれです。

リストをCollections.synchronizedListにすると、あまり効果がないと思います。

正解ですsynchronizedList。クリティカルセクションの粒度が細かすぎます。

自由に使える他の唯一のアプローチは、現在のリストをコピーし、そのコピーを変更してから、そのコピーをvolatile共有変数に割り当てる方法です。私はあなたがそのようなアプローチから利益を得るとは思わない。

于 2013-03-19T08:31:39.467 に答える
0

まず、countOpenConnections同期させることで問題解決します。

並行性を高めるためにできるもう1つのことは、connectionsフィールドの現在のリスト実装をCopyOnWriteArrayListに置き換えることです。これにより、synchronizedcountOpenConnectionsを削除できるため、競合ポイントが削除されます。ただし、これは、CopyOnWriteArrayListのオーバーヘッドのために、countConnections()がcloseConnectionよりもはるかに頻繁に呼び出される場合にのみ実際に意味があります。

于 2013-03-19T08:32:20.660 に答える