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
}

问题是,当多个线程访问单例时,一些调用同步了更改列表的方法,并且一些尝试计算打开的连接,这些连接有时会失败,因为同时列表被同步方法之一更改。

我相信仅仅使方法countOpenConnections同步也不能解决问题。我认为,列出清单 aCollections.synchronizedList也不会做太多事情。

你们中的一些人有什么方法可以帮助我吗?

4

4 回答 4

1

如果您可以制作List最终结果,则可以在列表本身上进行同步-这样,任何时候只有一个线程在列表中具有监视器。这种生硬的同步以增加锁竞争为代价解决了眼前的问题;任何时候只有一个线程可以访问List。但是为什么多个阅读线程不能同时访问列表-毕竟他们没有修改它...

输入ReentrantReadWriteLock,这将允许多个线程读取,但如果一个线程正在写入,则一切都必须等待。它有两种模式,“读”和“写”(因此得名)。这允许您将修改的方法与不修改的方法分开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 的使用List- 在您看来,您必须搜索List具有DbConnection特定 ID 的 a。不是Map更好的选择(恒定而不是线性时间搜索......)

    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同步解决问题。

增加并发性可以做的另一件事是connections用 CopyOnWriteArrayList 替换当前的字段列表实现。这允许您删除synchronizedon countOpenConnections,从而消除争用点。但是,由于 CopyOnWriteArrayList 的开销,这仅在 countConnections() 比 closeConnection 更频繁地被调用时才有意义。

于 2013-03-19T08:32:20.660 回答