java - 用 AtomicReference 代替 ReadWriteLock 来进行非阻塞操作
问题描述
当持久化的配置数据发生变化时,我编写了这个类来重新加载DataSource
整个应用程序使用的 。
如您所见,它由 管理CDI
并公开为Singleton
,并且“配置更改”事件通过configurationReload(...)
,但现在不相关。
参考更新由 a 保护ReentrantReadWriteLock
,但我想知道是否需要它。
@Singleton
@ThreadSafe
class ReloadingDataSource implements DataSource {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
@GuardedBy("readWriteLock")
private DataSource delegateDataSource;
@Inject
ReloadingDataSource(@Nonnull final Configuration configuration) {
delegateDataSource = createDataSource(configuration);
}
private DataSource createDataSource(final Configuration configuration) {
... Create a ComboPooledDataSource using properties extracted from Configuration.
}
@Override
public Connection getConnection() throws SQLException {
readLock.lock();
try {
return delegateDataSource.getConnection();
} finally {
readLock.unlock();
}
}
...
private void configurationReload(
@Observes @Reload final ConfigurationChanged configurationChanged,
@Nonnull final Configuration configuration) {
final ConfigurationEvent event = configurationChanged.getConfigurationEvent();
if (event.getType() != AbstractFileConfiguration.EVENT_RELOAD && !event.isBeforeUpdate()) {
return;
}
writeLock.lock();
try {
destroyDelegateDataSource();
delegateDataSource = createDataSource(configuration);
} finally {
writeLock.unlock();
}
}
private void destroyDelegateDataSource() {
try {
DataSources.destroy(delegateDataSource);
} catch (final SQLException ignored) {
// Do nothing.
}
}
}
如果我们忽略创建新 DataSource 的成本,上述策略是否可以替换为 a AtomicReference<DataSource>
,如下所示?
这将导致更好的性能和更容易阅读代码。
有没有更好的方法来处理这个我不知道的?
@Singleton
@ThreadSafe
class ReloadingDataSource implements DataSource {
private final AtomicReference<DataSource> delegateDataSource;
@Inject
ReloadingDataSource(@Nonnull final Configuration configuration) {
delegateDataSource = new AtomicReference<>(createDataSource(configuration));
}
private DataSource createDataSource(final Configuration configuration) {
... Create a ComboPooledDataSource using properties extracted from Configuration.
}
@Override
public Connection getConnection() throws SQLException {
return delegateDataSource.get().getConnection();
}
...
private void configurationReload(
@Observes @Reload final ConfigurationChanged configurationChanged,
@Nonnull final Configuration configuration) {
final ConfigurationEvent event = configurationChanged.getConfigurationEvent();
if (event.getType() != AbstractFileConfiguration.EVENT_RELOAD && !event.isBeforeUpdate()) {
return;
}
// Updated as per eckes tip. Is this what you meant?
final DataSource newDataSource = createDataSource(configuration);
while (true) {
final DataSource oldDataSource = delegateDataSource.get();
if (delegateDataSource.compareAndSet(oldDataSource, newDataSource)) {
destroyDelegateDataSource(oldDataSource);
break;
}
}
}
private void destroyDelegateDataSource(final DataSource oldDataSource) {
try {
DataSources.destroy(oldDataSource);
} catch (final SQLException ignored) {
// Do nothing.
}
}
}
解决方案
如果您需要以有序的方式处理更新,您仍然需要锁定 reload 方法。在这种情况下,您可以放弃 AtomicReference 逻辑而只使用 volatile:
public class RDS {
private volatile DataSource delegate;
public Connection getConnection() throws SQLException {
return delegate.getConnection();
}
private void reload(Configuration config) {
DataSource old = null;
synchronized(this) {
old = delegate;
delegate = createDataSource(config);
}
destroyDataSource(old);
}
}
但是请注意,您仍然可能遇到其他问题,当您关闭旧 DataSource 时,连接可能仍在使用中(在@eckes 对该问题的第一条评论中提到)。为了解决这个问题,您需要一个类似于具有获取/释放类型逻辑的连接池之类的东西,一旦所有现有连接都被释放,它就会关闭旧委托。
推荐阅读
- java - 如果 Spring Boot 微服务失去 MSSQL DB 连接,如何实现电子邮件/文本通知
- python - Python 线程仅在从另一个模块调用时才有效
- python - 如何在烧瓶响应中返回分块的二进制数据?
- java - 是否可以在 MR 作业之外将数据写入 HDFS 并仍然使用 Hive 进行查询?
- java - java.lang.OutOfMemoryErrorPyRaisable: java.lang.OutOfMemoryError: Java heap space 运行 jupyter notebook 时如何解决?
- javascript - 如何在更改输入时产生过渡效果
- git - 无法解决与 git 子模块文件夹的合并冲突
- python - 将特殊字符写入 csv 文件时出现问题
- python-3.x - Appium & Python:如何移动到另一个应用程序?
- php - (Swift_TransportException(code: 0): Connection to tcp://server.example.com:465 Timed Out