Java Selective Lock By ConcurrentHashMap and ReentrantLock

Cem Yasar
2 min readApr 9, 2022

In the previos posts, I have shared simple .NET solution about selective lock mechanism by using ConcurrentDictionary. Here, I share similar solution for Java implementation by using ConcurrentHashMap and ReentrantLock structures. This code is implemented in Java Spring Boot environment.

This example is more complex than previous .NET example, since in the previous post when a source is locked, the function returns error message as a string for other threads. In this example threads will wait for a specific resource in a selective manner. This approach can be accomplished in .NET example also by using lockable objects also.

For instance you may need to write a multiple datasource management in an app. This datasource manager should open connections to multiple different databases, but one time only. Many threads once to get connection to a specific datasource and if there is not any suitable connection for this datasource, the datasource manager should create one datasource for this specific database. If many threads try to connect to same database by using same datasource, the datasource manager should create only once datasource for those threads.

In this example Hikari connection pool is used to create datasources.

Here is the DataSourceManager.java class:

@Component
public class DataSourceManager {

//global private datasource list
//datasources are seperated with their ID values which are given by BLL and taken from datasourceList in a database
private Map<Integer, HikariDataSource> dataSourceList = new HashMap<>();
//global private ReentrantLock list for specific datasources
private static ConcurrentHashMap<Integer, ReentrantLock> datasourceCreationLockList = new ConcurrentHashMap<>();
//datasource creation method
public HikariDataSource getDataSource(DataSource dataSource) throws SQLException {
//if there is already a datasource exits for a given datasource ID, it wil lbe returned immediately
if(dataSourceList.get(dataSource.getId()) != null){
return dataSourceList.get(dataSource.getId());
}
//concurrentHashMap is thread-safe and it guarantees that only one value can be inserted for the same key
datasourceCreationLockList.putIfAbsent(dataSource.getId(), new ReentrantLock());
//here we lock the code instruction execution until the lock is unlocked//only the first thread is passed from here for this datasource until it unlocks the lockif(datasourceCreationLockList.get(dataSource.getId()) != null )
datasourceCreationLockList.get(dataSource.getId()).lock();
//we need to check again if there is one datasource for the specific datasource ID value.
//Since, after the creator thread unlocks the lock, other threads will continue to execute this fucntion and they must not allowed to reenter the datasource creation area.
if(dataSourceList.get(dataSource.getId()) != null){
return dataSourceList.get(dataSource.getId());
}
//datasource creations
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setUsername(dataSource.getDbUserName());
hikariDataSource.setPassword(dataSource.getDbUserPassword());
//native connection string creation
NativeDBAccessHelper dbAccessHelper = NativeDBAccessUtil.getNativeDBAccessHelper(dataSource.getDbType());
String dbDriverClassName = dbAccessHelper.getDriverClassName();
String jdbcConnectionString = dbAccessHelper.getJdbcConnectionString(dataSource.getHost(), dataSource.getPort(), dataSource.getDbName());
//setting datasource properties
hikariDataSource.setDriverClassName(dbDriverClassName);
hikariDataSource.setJdbcUrl(jdbcConnectionString);
hikariDataSource.setMinimumIdle(5);
hikariDataSource.setMaximumPoolSize(10);
hikariDataSource.setIdleTimeout(10000);
hikariDataSource.setConnectionTimeout(30000);
hikariDataSource.setMaxLifetime(30000);
//datasource is putted into global hashmap
dataSourceList.put(dataSource.getId(), hikariDataSource);
//the lock is unlocked then removed
datasourceCreationLockList.get(dataSource.getId()).unlock();
datasourceCreationLockList.remove(dataSource.getId());
return hikariDataSource;}
}

Thanks for reading,

Mustafa Cem Yaşar

--

--