如何使用 java 处理缓存中的锁 (ConcurrentHashMap)
How to handle lock in cache (ConcurrentHashMap) using java
我正在使用 concurrenthashmap
设计一个在多个线程之间共享的缓存系统。它还有两个方法,get 和 put。我无法处理一种情况。场景是,如果多个线程要从缓存中获取数据,而key不可用,那么一个线程会从数据库中获取数据,并将其放入缓存中(ConcurrentHashMap)
。其他线程将等到 thread-1
将数据设置到缓存中,然后其他线程将从缓存中读取数据。我将如何实现这一目标。
提前致谢。
您可以使用 java.util.concurrent
软件包提供的 ReadWriteLock
。
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
...
void write() {
readWriteLock.writeLock().lock();
try {
// read from db and write to cache
}
finally {
readWriteLock.writeLock().unlock();
}
}
void read() {
readWriteLock.readLock().lock();
try {
// read from cache or from the db
}
finally {
readWriteLock.readLock().unlock();
}
}
ConcurrentHashMap#computeIfAbsent
作为 , the ConcurrentHashMap
class offers a computeIfAbsent
方法来做你想做的事。该方法以原子方式工作:
- 查看地图是否有该键的条目。如果是这样,returns 该键的值。
- 如果未找到条目,则执行您指定的 lambda 函数以生成一个值。该值作为键值条目存储在映射中。并且,返回该值。
所有这些工作都是原子发生的,这意味着您的地图以线程安全的方式运行,无需添加任何进一步的保护。
引用 Javadoc:
If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null. The entire method invocation is performed atomically.
使用代码的方法引用从数据库中检索值的示例代码:
map.computeIfAbsent( myKey , key -> repository::fetchValueForKey ) ;
…或使用方法调用:
map.computeIfAbsent( myKey , key -> myRepository.fetchValueForKey( key ) ) ;
示例应用程序
这是一个完整的示例应用程序。
我们使用一个映射来跟踪哪个星期几被分配给哪个人的名字,将 String
映射到 java.time.DayOfWeek
枚举对象,Map< String , DayOfWeek >
。
我们从 Alice
和 Bob
的两个条目的映射开始。我们的目标是找到 Carol
的第三个条目。如果未找到,请为该键添加一个值为 DayOfWeek.THURSDAY
.
的条目
我们定义了一个 class Repository
,我们假装它正在调用数据库以查找分配给 Carol
.
键的值
我们要执行的任务被定义为一个Callable
那个returns一个DayOfWeek
对象。我们多次将 Callable
对象提交给执行程序服务。该服务 returns Future
对象,通过它我们可以跟踪成功并检索我们的结果(我们希望它是 DayOfWeek.THURSDAY
对象)。
为了显示结果,我们将 Map
连同每个 Future
.
的结果转储到控制台
package work.basil.demo.threadmark;
import java.time.DayOfWeek;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
public class MapApp
{
public static void main ( String[] args )
{
MapApp app = new MapApp();
app.demo();
}
private void demo ( )
{
Map < String, DayOfWeek > inputs =
Map.of(
"Alice" , DayOfWeek.MONDAY ,
"Bob" , DayOfWeek.TUESDAY
);
ConcurrentMap < String, DayOfWeek > map = new ConcurrentHashMap <>( inputs );
System.out.println( "INFO - Before: map = " + map );
Repository repository = new Repository();
ExecutorService executorService = Executors.newCachedThreadPool();
Callable < DayOfWeek > task = ( ) -> { return map.computeIfAbsent( "Carol" , ( String personNameKey ) -> {return repository.fetchDayOfWeekForPersonName( personNameKey ); } ); };
List < Callable < DayOfWeek > > tasks = List.of( task , task , task , task , task );
List < Future < DayOfWeek > > futures = List.of();
try
{
futures = executorService.invokeAll( tasks );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
executorService.shutdown();
try { executorService.awaitTermination( 10 , TimeUnit.SECONDS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "INFO - After: map = " + map );
futures.stream().forEach( dayOfWeekFuture -> {
try
{
System.out.println( dayOfWeekFuture.get() );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
catch ( ExecutionException e )
{
e.printStackTrace();
}
} );
}
class Repository
{
public DayOfWeek fetchDayOfWeekForPersonName ( final String personName )
{
return DayOfWeek.THURSDAY;
}
}
}
看到这个 code run live at IdeOne.com。
INFO - Before: map = {Bob=TUESDAY, Alice=MONDAY}
INFO - After: map = {Bob=TUESDAY, Alice=MONDAY, Carol=THURSDAY}
THURSDAY
THURSDAY
THURSDAY
THURSDAY
THURSDAY
我正在使用 concurrenthashmap
设计一个在多个线程之间共享的缓存系统。它还有两个方法,get 和 put。我无法处理一种情况。场景是,如果多个线程要从缓存中获取数据,而key不可用,那么一个线程会从数据库中获取数据,并将其放入缓存中(ConcurrentHashMap)
。其他线程将等到 thread-1
将数据设置到缓存中,然后其他线程将从缓存中读取数据。我将如何实现这一目标。
提前致谢。
您可以使用 java.util.concurrent
软件包提供的 ReadWriteLock
。
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
...
void write() {
readWriteLock.writeLock().lock();
try {
// read from db and write to cache
}
finally {
readWriteLock.writeLock().unlock();
}
}
void read() {
readWriteLock.readLock().lock();
try {
// read from cache or from the db
}
finally {
readWriteLock.readLock().unlock();
}
}
ConcurrentHashMap#computeIfAbsent
作为 ConcurrentHashMap
class offers a computeIfAbsent
方法来做你想做的事。该方法以原子方式工作:
- 查看地图是否有该键的条目。如果是这样,returns 该键的值。
- 如果未找到条目,则执行您指定的 lambda 函数以生成一个值。该值作为键值条目存储在映射中。并且,返回该值。
所有这些工作都是原子发生的,这意味着您的地图以线程安全的方式运行,无需添加任何进一步的保护。
引用 Javadoc:
If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null. The entire method invocation is performed atomically.
使用代码的方法引用从数据库中检索值的示例代码:
map.computeIfAbsent( myKey , key -> repository::fetchValueForKey ) ;
…或使用方法调用:
map.computeIfAbsent( myKey , key -> myRepository.fetchValueForKey( key ) ) ;
示例应用程序
这是一个完整的示例应用程序。
我们使用一个映射来跟踪哪个星期几被分配给哪个人的名字,将 String
映射到 java.time.DayOfWeek
枚举对象,Map< String , DayOfWeek >
。
我们从 Alice
和 Bob
的两个条目的映射开始。我们的目标是找到 Carol
的第三个条目。如果未找到,请为该键添加一个值为 DayOfWeek.THURSDAY
.
我们定义了一个 class Repository
,我们假装它正在调用数据库以查找分配给 Carol
.
我们要执行的任务被定义为一个Callable
那个returns一个DayOfWeek
对象。我们多次将 Callable
对象提交给执行程序服务。该服务 returns Future
对象,通过它我们可以跟踪成功并检索我们的结果(我们希望它是 DayOfWeek.THURSDAY
对象)。
为了显示结果,我们将 Map
连同每个 Future
.
package work.basil.demo.threadmark;
import java.time.DayOfWeek;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
public class MapApp
{
public static void main ( String[] args )
{
MapApp app = new MapApp();
app.demo();
}
private void demo ( )
{
Map < String, DayOfWeek > inputs =
Map.of(
"Alice" , DayOfWeek.MONDAY ,
"Bob" , DayOfWeek.TUESDAY
);
ConcurrentMap < String, DayOfWeek > map = new ConcurrentHashMap <>( inputs );
System.out.println( "INFO - Before: map = " + map );
Repository repository = new Repository();
ExecutorService executorService = Executors.newCachedThreadPool();
Callable < DayOfWeek > task = ( ) -> { return map.computeIfAbsent( "Carol" , ( String personNameKey ) -> {return repository.fetchDayOfWeekForPersonName( personNameKey ); } ); };
List < Callable < DayOfWeek > > tasks = List.of( task , task , task , task , task );
List < Future < DayOfWeek > > futures = List.of();
try
{
futures = executorService.invokeAll( tasks );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
executorService.shutdown();
try { executorService.awaitTermination( 10 , TimeUnit.SECONDS ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "INFO - After: map = " + map );
futures.stream().forEach( dayOfWeekFuture -> {
try
{
System.out.println( dayOfWeekFuture.get() );
}
catch ( InterruptedException e )
{
e.printStackTrace();
}
catch ( ExecutionException e )
{
e.printStackTrace();
}
} );
}
class Repository
{
public DayOfWeek fetchDayOfWeekForPersonName ( final String personName )
{
return DayOfWeek.THURSDAY;
}
}
}
看到这个 code run live at IdeOne.com。
INFO - Before: map = {Bob=TUESDAY, Alice=MONDAY}
INFO - After: map = {Bob=TUESDAY, Alice=MONDAY, Carol=THURSDAY}
THURSDAY
THURSDAY
THURSDAY
THURSDAY
THURSDAY