首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用ReadWriteLock代替AtomicReference进行非阻塞操作。

用ReadWriteLock代替AtomicReference进行非阻塞操作。
EN

Stack Overflow用户
提问于 2018-10-08 18:02:44
回答 1查看 469关注 0票数 2

当持久化配置数据发生变化时,我编写了这个类来重新加载整个应用程序使用的DataSource

如您所见,它由CDI管理,并作为一个Singleton公开,“配置更改”事件通过configurationReload(...)方法到达,但现在已经不相关了。

参考更新由ReentrantReadWriteLock保护,但我想知道是否需要它。

代码语言:javascript
复制
@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的成本,那么上面的策略是否会被一个AtomicReference<DataSource>所取代,如下所示?

它将导致更好的性能和更容易阅读的代码。

有没有更好的方法来处理这些我不知道的事情?

代码语言:javascript
复制
@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.
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-10-08 20:08:12

如果需要以有序的方式处理更新,则仍然需要锁定重新加载方法。在这种情况下,您可以放弃AtomicReference逻辑,只需使用易失性:

代码语言:javascript
复制
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第一条注释中提到)。为了解决这个问题,您需要类似于带有获取/释放类型逻辑的连接池( connections ),该逻辑一旦释放了所有现有连接,就会关闭旧的委托。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52707808

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档