java - 使用 AtomicReference 替换 ReadWriteLock 以实现非阻塞操作

标签 java multithreading concurrency

我写这个类是为了重新加载 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/

相关文章:

multithreading - 线程之间共享哪些资源?

java - Android Studio3.2 APK Build Error -> 保留文件或目录名 'lib'

java - 使用页面工厂处理分页

java - 在使用 java.util.concurrent 类时,我应该同步以避免可见性问题吗?

go - channel 已关闭,但所有 goroutine 都处于 sleep 状态 - 死锁

java - 静态方法调用单例

linux - 使用建议文件锁限制并发进程

java - ant 中的 unix2dos(或 dos2unix)

java - 在 TableView Javafx 中插入数据

c++ - 在 C++11 中,您能否将引用的基类传递给线程的构造函数并获得多态行为?