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;
}