我写这个类是为了重新加载 DataSource
,当持久化配置数据发生更改时,由整个应用程序使用。
如您所见,它由 CDI
管理。并公开为 Singleton
,“配置更改”事件通过 configurationReload(...)
到达方法,但现在不相关。
引用更新由 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.
}
}
}
如果我们忽略创建新数据源的成本,上述策略是否可以替换为 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.
}
}
}
最佳答案
如果您需要以有序的方式处理更新,您仍然需要锁定重新加载方法。在这种情况下,您可以放弃 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);
}
}
但请注意,您仍然可能遇到其他问题,即当您关闭旧数据源时,连接可能仍用于旧数据源(@eckes 对问题的第一条评论中提到)。为了解决这个问题,您需要一个具有获取/释放类型逻辑的连接池之类的东西,一旦所有现有连接被释放,它就会关闭旧的委托(delegate)。
关于java - 使用 AtomicReference 替换 ReadWriteLock 以实现非阻塞操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52707808/