ConcurrentHashMap#computeIfAbsent
作为commented by Wasserman,ConcurrentHashMap 类提供了一个computeIfAbsent 方法来做你想做的事。该方法以原子方式工作:
- 查看映射是否有该键的条目。如果是,则返回该键的值。
- 如果未找到条目,则执行您指定的 lambda 函数以生成一个值。该值存储为映射中的键值条目。并且,该值被返回。
所有这些工作都以原子方式发生,这意味着您的地图以线程安全的方式运行,无需您添加任何进一步的保护。
引用 Javadoc:
如果指定的键尚未与值关联,则尝试使用给定的映射函数计算其值并将其输入到此映射中,除非为空。整个方法调用以原子方式执行。
示例代码使用方法引用为您的代码从数据库中检索值:
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 的条目。
我们定义了一个类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