【问题标题】:How to handle lock in cache (ConcurrentHashMap) using java如何使用java处理缓存中的锁定(ConcurrentHashMap)
【发布时间】:2021-07-30 15:16:44
【问题描述】:

我正在使用concurrenthashmap 设计一个缓存系统,它在多个线程之间共享。它也有两个方法,get 和 put。我无法处理一种情况。场景是,如果多个线程要从缓存中获取数据,并且key不可用,那么一个线程会从数据库中获取数据并放入缓存(ConcurrentHashMap)。其他线程将等到thread-1 将数据设置到缓存中,然后其他线程将从缓存中读取数据。我将如何实现这一目标。

提前致谢。

【问题讨论】:

标签: java multithreading caching concurrenthashmap in-memory


【解决方案1】:

ConcurrentHashMap#computeIfAbsent

作为commented by WassermanConcurrentHashMap 类提供了一个computeIfAbsent 方法来做你想做的事。该方法以原子方式工作:

  • 查看映射是否有该键的条目。如果是,则返回该键的值。
  • 如果未找到条目,则执行您指定的 lambda 函数以生成一个值。该值存储为映射中的键值条目。并且,该值被返回。

所有这些工作都以原子方式发生,这意味着您的地图以线程安全的方式运行,无需您添加任何进一步的保护。

引用 Javadoc:

如果指定的键尚未与值关联,则尝试使用给定的映射函数计算其值并将其输入到此映射中,除非为空。整个方法调用以原子方式执行。

示例代码使用方法引用为您的代码从数据库中检索值:

map.computeIfAbsent( myKey , key -> repository::fetchValueForKey ) ;

…或者使用方法调用:

map.computeIfAbsent( myKey , key -> myRepository.fetchValueForKey( key ) ) ;

示例应用

这是一个完整的示例应用程序。

我们使用地图来跟踪将哪一天分配给哪个人的姓名,将String 映射到java.time.DayOfWeek 枚举对象,Map< String , DayOfWeek >

我们从AliceBob 的两个条目的映射开始。我们的目标是为Carol 找到第三个条目。如果未找到,请为该键添加一个值为 DayOfWeek.THURSDAY 的条目。

我们定义了一个类Repository,我们假装它正在调用一个数据库来查找分配给Carol键的值。

我们要执行的任务被定义为一个Callable,它返回一个DayOfWeek 对象。我们将Callable 对象多次提交给执行器服务。该服务返回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

【讨论】:

    【解决方案2】:

    您可以使用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();
      }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-14
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      • 1970-01-01
      相关资源
      最近更新 更多