如何使用java處理緩存中的鎖定(ConcurrentHashMap) (How to handle lock in cache (ConcurrentHashMap) using java)


問題描述

如何使用java處理緩存中的鎖定(ConcurrentHashMap) (How to handle lock in cache (ConcurrentHashMap) using java)

我正在使用在多個線程之間共享的 concurrenthashmap 設計一個緩存系統。它也有兩個方法,get 和 put。我無法處理一種情況。場景是,如果多個線程要從緩存中獲取數據,而key不可用,那麼一個線程會從數據庫中獲取數據並放入緩存(ConcurrentHashMap)。其他線程將等待,直到 thread‑1 將數據設置到緩存中,然後其他線程將從緩存中讀取數據。我將如何實現這一目標。

提前致謝。


參考解法

方法 1:

ConcurrentHashMap#computeIfAbsent

As commented by Wasserman, the ConcurrentHashMap class offers a computeIfAbsent method to do just what you want. The method works atomically to:

  • See if the map has an entry for that key. If so, returns the value for that key.
  • If no entry found, executors your specified lambda function to produce a value. That value is stored as a key‑value entry in the map. And, that value is returned.

  • </ul>

    All that work happens atomically, meaning that your map operates in a thread‑safe manner without you needing to add any further protection.

    To quote the 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.


    </blockquote>

    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 >.

    We start with a map of two entries for Alice & Bob. Our goal is to find a third entry for Carol. If not found, add an entry for that key with a value of 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.

    Our task to be executed is defined as a Callable that returns a DayOfWeek object. We submit our Callable object several times to an executor service. That service returns Future objects through which we can track success and retrieve our result (which we expect to be DayOfWeek.THURSDAY object).

    To show results, we dump the Map to console, along with the result of each 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.

    INFO ‑ Before: map = {Bob=TUESDAY, Alice=MONDAY}
    INFO ‑ After: map = {Bob=TUESDAY, Alice=MONDAY, Carol=THURSDAY}
    THURSDAY
    THURSDAY
    THURSDAY
    THURSDAY
    THURSDAY
    

    方法 2:

    You can use ReadWriteLock that is provided by java.util.concurrent package.

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

    (by user15439867Basil BourqueSemyon Kirekov)

    參考文件

    1. How to handle lock in cache (ConcurrentHashMap) using java (CC BY‑SA 2.5/3.0/4.0)

#caching #java #concurrenthashmap #in-memory #multithreading






相關問題

Heroku 上的頁面緩存技巧? (Page caching trick on Heroku?)

Array of Structs selalu lebih cepat daripada Structs of arrays? (Array of Structs are always faster than Structs of arrays?)

使用 Varnish 更改標頭中的引用者 (Change Referrer in header using Varnish)

清理 ios 中的 uiwebview 緩存 (clean uiwebview cache in ios)

緩存整個表 (Caching the entire table)

過期/緩存控制標頭的問題 (Problem with Expires/Cache-Control Headers)

強制 L1 緩存上的一些數據 (force some data on L1 cache)

Facebook API - 在服務器上緩存響應 (Facebook API - cache response on server)

ASIHTTPRequest 離線模式連接失敗 (ASIHTTPRequest offline mode connection failure)

如果小於 X 天,如何從磁盤讀取文件,如果舊,則重新獲取 html 文件 (How to read a file from the disk if less than X days old, if older, refetch the html file)

當您的應用服務器託管在不同的雲服務上時,如何安全地從 Firebase 託管上的 CDN 緩存中受益 (How to safely benefit from CDN caching on Firebase Hosting when your app's server is hosted on a different Cloud service)

如何使用java處理緩存中的鎖定(ConcurrentHashMap) (How to handle lock in cache (ConcurrentHashMap) using java)







留言討論