简体   繁体   English

如何使用 java 处理缓存中的锁定 (ConcurrentHashMap)

[英]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:该方法以原子方式工作以:

  • See if the map has an entry for that key.查看 map 是否有该键的条目。 If so, returns the value for that key.如果是,则返回该键的值。
  • If no entry found, executors your specified lambda function to produce a value.如果未找到条目,则执行您指定的 lambda function 以产生一个值。 That value is stored as a key-value entry in the map.该值作为键值条目存储在 map 中。 And, that value is returned.并且,该值被返回。

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 ) ) ;

Example app示例应用

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 .我们从AliceBob的两个条目的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM