Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用同步和非同步方法的ConcurrentModificationException_Java_Multithreading - Fatal编程技术网

Java 使用同步和非同步方法的ConcurrentModificationException

Java 使用同步和非同步方法的ConcurrentModificationException,java,multithreading,Java,Multithreading,所以我们有一个整洁的类,它看起来类似于: class ConnectionObserver { private List<DbConnection> connections; // singleton logic public synchronized void closeConnection(int id) { for(Iterator<DbConnection> it = connections.iterator(): it

所以我们有一个整洁的类,它看起来类似于:

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
}
类连接观察服务器{
私有列表连接;
//单态逻辑
公共同步无效关闭连接(int id){
for(Iterator it=connections.Iterator():it.hasNext()){
DbConnection conn=it.next();
if(conn.getId()==id){
康涅狄格州关闭();
it.remove();
}
}
}
public int countOpenConnections(){
int open=0;
用于(数据库连接连接:连接){
如果(连接等参线()){
++开放的;
}
}
返回打开;
}
//通过迭代器改变列表的更多同步方法
}
问题是,当多个线程访问singleton时,有些调用同步了改变列表的方法,有些尝试计数打开的连接,有时会失败,因为其中一个同步方法同时改变了列表

我确信,仅仅使方法
countOpenConnections
同步也不能解决问题。将列表设置为集合。我认为同步列表不会做太多


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

每次关闭连接时,都会将其从列表中删除。所以只需返回连接的大小。此外,作为连接观察者,您可以侦听连接关闭事件,并仅使用打开的连接更新列表

我确信仅仅使countOpenConnections方法同步也不能解决问题

实际上,这正是使程序正确同步所需要的

我想,让列表成为一个集合。synchronizedList不会做太多

你说得对:
synchronizedList
会给你提供太细的关键部分粒度


您可以使用的唯一其他方法是复制当前列表、修改副本,然后将副本分配给
volatile
共享变量。我认为你不会从这种方法中受益。

首先,使
countOpenConnections
同步将解决问题


要提高并发性,可以做的另一件事是用CopyOnWriteArrayList替换
connections
字段的当前列表实现。这允许您在countOpenConnections上删除
同步的
,从而删除争用点。但是,由于CopyOnWriteArrayList的开销,只有在调用countConnections()的频率比closeConnection高得多的情况下,这才有意义。

如果您可以将
列表设置为最终列表,则可以在列表本身上进行同步-这样,在任何时候只有一个线程在列表上有监视器。这种钝同步以增加锁争用为代价解决了当前的问题;一次只能有一个线程访问
列表
。但是为什么多个阅读线程不能同时访问列表呢?毕竟它们并没有修改列表

输入,这将允许多个线程读取,但如果一个线程正在写入,则所有内容都必须等待。它有两种模式,“读”和“写”(因此得名)。这允许您将修改
列表的方法与不修改列表的方法分开-减少锁争用

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
}

如果他想保持方法的当前语义,我看不出这对ConcurrentModificationException有什么帮助。显然,这个答案要求改变语义。OP这样做可能是有原因的。但是,如果唯一的目的是获取打开连接的数量,那么可以考虑直接获取连接列表的大小。stacktrace在哪里?感谢@ShiDoiSi的努力,但我认为,在这种特殊情况下,堆栈跟踪没有那么重要。我今天学到的教训是:
synchronize
ing类的方法意味着锁定对相同和其他
synchronized
方法的其他访问。我认为这样的锁只对特定的方法有效。那么你应该从问题标题中删除例外。谢谢你的详细回答。我期待着把这些东西换成地图。手动锁定部分代码看起来也很有趣。简短反馈:在整个类及其方法中实现
ReentrantReadWriteLock
。使用10k个条目和四个线程进行测试,每个线程访问一个方法10k次。工作起来很有魅力。
    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;
    }