[英]How to handle lock in cache (ConcurrentHashMap) using java
I am designing a cache system using concurrenthashmap
which is shared among multiple threads.我正在使用在多个线程之间共享的
concurrenthashmap
设计一个缓存系统。 It also has two methods, get and put.它也有两个方法,get 和 put。 I am unable to handle one scenario.
我无法处理一种情况。 The scenario is, if multiple threads want to get data from the cache and the key is not available, then one thread will get data from the database and put it into thecache
(ConcurrentHashMap)
.场景是,如果多个线程要从缓存中获取数据,并且key不可用,那么一个线程会从数据库中获取数据并放入缓存中
(ConcurrentHashMap)
。 The other threads will wait until thread-1
sets data into the cache then other threads will read data from the cache.其他线程将等待直到
thread-1
将数据设置到缓存中,然后其他线程将从缓存中读取数据。 How will I achieve this.我将如何实现这一目标。
Thanks in advance.提前致谢。
ConcurrentHashMap#computeIfAbsent
As commented by Wasserman , the ConcurrentHashMap
class offers a computeIfAbsent
method to do just what you want.正如Wasserman 所评论的那样,
ConcurrentHashMap
class 提供了一个computeIfAbsent
方法来做你想做的事。 The method works atomically to:该方法以原子方式工作以:
All that work happens atomically, meaning that your map operates in a thread-safe manner without you needing to add any further protection.所有这些工作都是原子发生的,这意味着您的 map 以线程安全的方式运行,您无需添加任何进一步的保护。
To quote the Javadoc:引用 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.
如果指定的键尚未与值关联,则尝试使用给定的映射 function 计算其值,并将其输入到此 map 中,除非 null。 The entire method invocation is performed atomically.
整个方法调用以原子方式执行。
Example code using a method-reference for your code to retrieve a value from the database:示例代码使用代码的方法引用从数据库中检索值:
map.computeIfAbsent( myKey , key -> repository::fetchValueForKey ) ;
… or use a method call: …或使用方法调用:
map.computeIfAbsent( myKey , key -> myRepository.fetchValueForKey( key ) ) ;
Here is a complete example app.这是一个完整的示例应用程序。
We use a map of tracking which day-of-week is assigned to which person's name, mapping a String
to a java.time.DayOfWeek
enum object, a Map< String, DayOfWeek >
.我们使用 map 跟踪哪个星期几分配给哪个人的姓名,将
String
映射到java.time.DayOfWeek
枚举 ZA8CFDE6331BD59EB2AC96F8911C4OfWeek Map< String, DayOfWeek >
We start with a map of two entries for Alice
& Bob
.我们从
Alice
和Bob
的两个条目的 map 开始。 Our goal is to find a third entry for Carol
.我们的目标是为
Carol
找到第三个条目。 If not found, add an entry for that key with a value of DayOfWeek.THURSDAY
.如果未找到,请为该键添加一个值为
DayOfWeek.THURSDAY
的条目。
We define a class Repository
which we pretend is doing a call to a database to lookup the value assigned to key of Carol
.我们定义了一个 class
Repository
,我们假装它正在调用数据库以查找分配给Carol
键的值。
Our task to be executed is defined as a Callable
that returns a DayOfWeek
object.我们要执行的任务被定义为一个返回
DayOfWeek
object 的Callable
。 We submit our Callable
object several times to an executor service.我们将
Callable
object 多次提交给执行器服务。 That service returns Future
objects through which we can track success and retrieve our result (which we expect to be DayOfWeek.THURSDAY
object).该服务返回
Future
对象,我们可以通过这些对象跟踪成功并检索我们的结果(我们希望它是DayOfWeek.THURSDAY
对象)。
To show results, we dump the Map
to console, along with the result of each Future
.为了显示结果,我们将
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;
}
}
}
See this code run live at IdeOne.com .请参阅在 IdeOne.com 上实时运行的代码。
INFO - Before: map = {Bob=TUESDAY, Alice=MONDAY}
INFO - After: map = {Bob=TUESDAY, Alice=MONDAY, Carol=THURSDAY}
THURSDAY
THURSDAY
THURSDAY
THURSDAY
THURSDAY
You can use ReadWriteLock
that is provided by java.util.concurrent
package.您可以使用
java.util.concurrent
package 提供的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();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.