所以我们有一个看起来类似的整洁的课程:
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
同步也无法解决问题。我认为,将列表设为Collections.synchronizedList
也不会做太多。
你们中有些人有办法可以帮助我吗?
答案 0 :(得分: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
以获取相应锁定的任何其他方法,并删除所有List
个关键字。
另一方面,我不理解使用List
- 您似乎必须在DbConnection
中搜索具有特定ID的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;
}
不是更好的选择(常数而不是线性时间搜索......)
{{1}}
答案 1 :(得分:0)
每次关闭连接时,都会将其从列表中删除。所以只需返回连接的大小。此外,作为一个连接观察者,您可以监听连接关闭事件并仅使用打开的连接更新列表。
答案 2 :(得分:0)
我确信只使countOpenConnections方法同步也无法解决问题。
实际上,这正是您正确同步程序所需的。
将列表设为Collections.synchronizedList也不会做太多,我想。
你是对的:synchronizedList
会给你一个太精细的关键部分。
您可以使用的唯一其他方法是复制当前列表,修改副本,然后将副本分配给volatile
共享变量。我不认为你会从这种方法中受益。
答案 3 :(得分:0)
首先,让countOpenConnections
同步将解决问题。
您可以做的另一件事是增加并发性,即使用CopyOnWriteArrayList替换connections
字段的当前列表实现。这允许您在countOpenConnections上删除synchronized
,从而删除争用点。但是,由于CopyOnWriteArrayList的开销,如果countConnections()的调用次数比closeConnection频繁得多,那么这才有意义。