Vous êtes sur la page 1sur 5

ConcurrentHashMapinJava8

ConcurrentHashMaphasbeenaverypopulardatastructurethesedays.Inthistalk,wewillwalkthroughthenewfeaturesthatgotintroducedinJava8.

Java8introducedtheforEach,search,andreducemethods,whichareprettymuchtosupportparallelism.Thesethreeoperationsareavailableinfour
forms:acceptingfunctionswithkeys,values,entries,andkeyvaluepairarguments.

AllofthosemethodstakeafirstargumentcalledparallelismThreshold.

Wewilltaketheexampleofthismapdefinition:

1 ConcurrentHashMap<String,Integer>hashMap=newConcurrentHashMap<>();
2 hashMap.put("A",1);
3 hashMap.put("B",2);
4 hashMap.put("C",3);
5 hashMap.put("D",4);
6 hashMap.put("E",5);
7 hashMap.put("F",6);
8 hashMap.put("G",7);

parallelismThreshold
Thisistodefinehowyouwantedtoexecutetheoperationssequentiallyorinparallel.SupposeyouhavegivenaparallelismThresholdas2.Soaslongas
therearefewerthantwoelementsinyourmap,itwouldbesequential.Otherwise,it'sparallel(dependsontheJVM).

1 hashMap.forEach(2,(k,v)>System.out.println("key>"+k+"isrelatedwithvalue>"+v+",bythread>"+Thread.currentThread().getName()));

Itproducedthebelowo/ponmymachine(youcanseetwodifferentthreadsinactionmainandForkJoinPool.commonPoolworker1):

1 key>Aisrelatedwithvalue>1,bythread>main
2 key>Disrelatedwithvalue>4,bythread>ForkJoinPool.commonPoolworker1
3 key>Bisrelatedwithvalue>2,bythread>main
4 key>Eisrelatedwithvalue>5,bythread>ForkJoinPool.commonPoolworker1
5 key>Cisrelatedwithvalue>3,bythread>main
6 key>Fisrelatedwithvalue>6,bythread>ForkJoinPool.commonPoolworker1
7 key>Gisrelatedwithvalue>7,bythread>ForkJoinPool.commonPoolworker1

forEach
Thismethodhasthesignature:

publicvoidforEach(longparallelismThreshold,BiConsumeraction)

WehavealreadyseentheparallelismThreshold.Now,let'sdiveintoBiConsumer.It'saFunctionalInterfacethatacceptstwoinputargumentsandreturnsno
result.Ithasthisdefinition:

1 @FunctionalInterface
2 publicinterfaceBiConsumer<T,U>{
3 voidaccept(Tt,Uu);
4 }

SoforEachtakesparallelismThresholdandBiConsumerandcallsthismethod:

1 newForEachMappingTask(null,batchFor(parallelismThreshold),0,0,table,action).invoke();

ThebatchFor(parallelismThreshold)methodistogetparallelismusingtheForkJoinPool.getCommonPoolParallelism()method,aswehaveseenabovein
theparallelismThresholdexample.

Here, action istheBiConsumerthatwepassedinforEachmethod.

ForEachMappingTaskisastaticfinalclassthatextendsBulkTask,andBulkTaskextendstheabstractclassCountedCompleter,whichextendstheabstract
classForkJoinTask.Soinshort,ForEachMappingTaskistosupportForkJoinTask.

TheForEachMappingTaskclasshasthemethodcomputeasbelow:

1 publicfinalvoidcompute(){
2 finalBiConsumer<?superK,?superV>action;
3 if((action=this.action)!=null){
4 for(inti=baseIndex,f,h;batch>0&&
5 (h=((f=baseLimit)+i)>>>1)>i;){
5 (h=((f=baseLimit)+i)>>>1)>i;){
6 addToPendingCount(1);
7 newForEachMappingTask<K,V>(this,batch>>>=1,
8 baseLimit=h,f,tab,action).fork();
9 }
10 for(Node<K,V>p;(p=advance())!=null;)
11 action.accept(p.key,p.val);
12 propagateCompletion();
13 }
14 }

Eachtimeweexecuteasubtask,theaddToPendingCount(int)methodgetsinvokedasintheabovecode.Thisisjusttohaveacounter,whichistodetermine
iftaskexecutioniscompleted.

Thecomputemethodaboveisresponsibleforaddingthesubtasktoaqueue(shared/unsharedqueue)usingtheforkmethodofForkJoinTask,asseeninthe
abovecode.

newForEachMappingTask(this,batch>>>=1,baseLimit=h,f,tab, action).fork();

Let'sunderstandtheBiConsumer'sacceptmethodwiththeexamplebelow:

1 importjava.util.function.BiConsumer;
2 publicclassBiConsumerTest{
3 publicstaticvoidmain(String[]args){
4 BiConsumer<String,String>biConsumer=(x,y)>{
5 System.out.println("Key=>"+x+",andvalue=>"+y);
6 };
7 biConsumer.accept("k","arun");
8 }
9 }

OtherForkJoinTasks,suchasRecursiveTaskandRecursiveAction,don'tneedanyexplicitcalltofinishtheirjob,whileinthecaseofCountedCompleter,an
explicitcallofpropagateCompletion()isrequired.

It'sthesamethingyou'veseeninthecomputemethod.

Aswehaveseenabove,callingtheinvokemethodofForEachMappingTask,sinceForEachMappingTaskextendsForkJoinTaskimplementationasbelow

1 publicfinalVinvoke(){
2 ints;
3 if((s=doInvoke()&DONE_MASK)!=NORMAL)
4 reportException(s);
5 returngetRawResult();
6 }

Here,getRawResult()returnstheresultofthecomputation,whichisnullbydefault.Otherwise,itshouldbeoverridden.

Search
Let'sseeasearchsnippet:

1 Stringresult=hashMap.search(1,(k,v)>{
2 System.out.println(Thread.currentThread().getName());
3 if(k.equals("A"))
4 returnk+""+v;
5 returnnull;
6 });
7 System.out.println("result=>"+result);

O/P:

1 main
2 ForkJoinPool.commonPoolworker2
3 ForkJoinPool.commonPoolworker2
4 result=>A1

Themethodsignatureofthesearchmethodis:

publicUsearch(longparallelismThreshold,BiFunctionsearchFunction)

ThesearchmethodcallstheinvokemethodofSearchMappingsTask.SearchMappingsTaskisasubclassofBulkTask,whichwehaveseenalready.
SearchmappingTaskhasacomputemethodasbelow:

1 publicfinalvoidcompute(){
2 finalBiFunction<?superK,?superV,?extendsU>searchFunction;
3 finalAtomicReference<U>result;
3 finalAtomicReference<U>result;
4 if((searchFunction=this.searchFunction)!=null&&
5 (result=this.result)!=null){
6 for(inti=baseIndex,f,h;batch>0&&
7 (h=((f=baseLimit)+i)>>>1)>i;){
8 if(result.get()!=null)
9 return;
10 addToPendingCount(1);
11 newSearchMappingsTask<K,V,U>(this,batch>>>=1,
12 baseLimit=h,f,tab,searchFunction,result).fork();
13 }
14 while(result.get()==null){
15 Uu;
16 Node<K,V>p;
17 if((p=advance())==null){
18 propagateCompletion();
19 break;
20 }
21 if((u=searchFunction.apply(p.key,p.val))!=null){
22 if(result.compareAndSet(null,u))
23 quietlyCompleteRoot();
24 break;
25 }
26 }
27 }
28 }

Inabovemethod,AtomicReferenceisusedtohaveanobjectreference,whichmaybeupdatedatomically,sinceit'saresult.Eachtimeasubtaskisexecuted,
theaddToPendingCount(int)methodisgettinginvokedasintheabovecode.Thisisjusttohaveacountertodetermineiftaskexecutionisgoingwell.And
afterthat,it'scallingtheforkmethodtomakeitparallel.

OtherForkJoinTasks,suchasRecursiveTaskandRecursiveAction,don'tneedanyexplicitcalltofinishtheirjob,whileinthecaseofCountedCompleter,an
explicitcallofpropagateCompletion()isrequired.Later,thequietlyCompleteRoot()methodiscompletesthetasknormally.

Merge
AspertheJavadoc,"Ifthespecifiedkeyisnotalreadyassociatedwitha(nonnull)value,associatesitwiththegivenvalue.Otherwise,replacesthevaluewith
theresultsofthegivenremappingfunction,orremovesifnull.Theentiremethodinvocationisperformedatomically.Someattemptedupdateoperationson
thismapbyotherthreadsmaybeblockedwhilecomputationisinprogress,sothecomputationshouldbeshortandsimple,andmustnotattempttoupdate
anyothermappingsofthisMap."

Letuslookatthecodesnippetforthesame:

1 ConcurrentHashMap<String,String>map=newConcurrentHashMap<>();
2 map.put("X","x");
3 System.out.println("1st==>"+map);
4 System.out.println("2nd==>"+map.merge("X","x",(v1,v2)>null));
5 System.out.println("3rd==>"+map);
6 map.put("Y","y");
7 map.put("X","x1");
8 System.out.println("4th==>"+map.merge("X","x1",(v1,v2)>"z"));
9 System.out.println("5th==>"+map);
10 System.out.println("6th==>"+map.merge(
11 "X","x1",(v1,v2)>v2.concat("z")));
12 System.out.println("7th==>"+map);

O/P:

1 1st==>{X=x}
2 2nd==>null
3 3rd==>{}
4 4th==>z
5 5th==>{X=z,Y=y}
6 6th==>x1z
7 7th==>{X=x1z,Y=y}

Themethodsignatureofthemergemethodis:

publicVmerge(Kkey,Vvalue,BiFunctionremappingFunction)

Here,remappingFunctionisthefunctionthatrecomputesavalueifpresent.

getOrDefault
AspertheJavadoc,"Itreturnsthevaluetowhichthespecifiedkeyismapped,orthegivendefaultvalueifthismapcontainsnomappingforthekey."
AspertheJavadoc,"Itreturnsthevaluetowhichthespecifiedkeyismapped,orthegivendefaultvalueifthismapcontainsnomappingforthekey."

Letuslookatthecodesnippet:

1 ConcurrentHashMap<String,Integer>defaultMap=
2 newConcurrentHashMap<String,Integer>();
3 defaultMap.put("X",30);
4 System.out.println(defaultMap);
5 System.out.println(defaultMap.getOrDefault("Y",21));

O/P:

1 {X=30}
2 21

Compute
Generally,wedosomecomputationonmapvaluesandstoreitback.Intheconcurrentmodel,it'sdifficulttomanage,andthat'sthereasonJavaintroduced
thecomputemethod.Theentiremethodinvocationisperformedatomically.

ThecomputeandcomputeIfPresentmethodstakearemappingfunctionasanargumenttocomputeavalue,andremappingisoftypeBiFunction.The
computeIfAbsentmethodtakesanargumentasmappingFunctiontocomputeavalue,andhencemappingFunctionisoftypeFunction.

Letuslookatthecodesnippettounderstandthis:

1 ConcurrentHashMap<String,Integer>map1=newConcurrentHashMap<>();
2 map1.put("A",1);
3 map1.put("B",2);
4 map1.put("C",3);
5 //Computeanewvaluefortheexistingkey
6 System.out.println("1stprint=>"+map1.compute("A",
7 (k,v)>v==null?42:v+40));
8 System.out.println("2ndprint=>"+map1);
9 //Thiswilladdanew(key,value)pair
10 System.out.println("3rdprint=>"+map1.compute("X",
11 (k,v)>v==null?42:v+41));
12 System.out.println("4thprint=>"+map1);
13
14 //computeIfPresentmethod
15 System.out.println("5thprint=>"+map1.computeIfPresent("X",(k,v)>v==null?42:v+10));
16 System.out.println("6thprint=>"+map1);
17
18 //computeIfAbsentmethod
19 System.out.println("7thprint=>"+map1.computeIfAbsent("Y",(k)>90));
20 System.out.println("8thprint=>"+map1);

O/P:

1 1stprint=>41
2 2ndprint=>{A=41,B=2,C=3}
3 3rdprint=>42
4 4thprint=>{A=41,B=2,C=3,X=42}
5 5thprint=>52
6 6thprint=>{A=41,B=2,C=3,X=52}
7 7thprint=>90
8 8thprint=>{A=41,B=2,C=3,X=52,Y=90}

Reduce
Thereducemethodsignatureis:

1 publicUreduce(longparallelismThreshold,BiFunctiontransformer,BiFunctionreducer)

Here,transformerisafunctionreturningthetransformationforanelement,ornullifthereisnotransformation(inwhichcaseitisnotcombined),and
reducerisacommutativeassociativecombiningfunction.

ThereducemethodcallstheMapReduceMappingsTask'sinvokemethod.MapReduceMappingsTaskextendsBulkTask,andwehaveseenthisalready.

Letuslookattheexamplebelow:

1 ConcurrentHashMap<String,Integer>reducedMap=newConcurrentHashMap<>();
2 reducedMap.put("One",1);
3 reducedMap.put("Two",2);
4 reducedMap.put("Three",3);
4 reducedMap.put("Three",3);
5 System.out.println("reduceexample=>"
6 +reducedMap.reduce(2,(k,v)>v*2,(total,elem)>total+elem));
7
8 System.out.println("reduceKeysexample=>"
9 +reducedMap.reduceKeys(2,(key1,key2)>key1.length()>key2.length()?key1+""+key2:key2+""+key1));
10
11 System.out.println("reduceValuesexample=>"
12 +reducedMap.reduceValues(2,(v)>v*2,(value1,value2)>value1>value2?value1value2:value2value1));
13 System.out.println("Afterreduce=>"+reducedMap);

O/P:

1 reduceexample=>12
2 reduceKeysexample=>ThreeTwoOne
3 reduceValuesexample=>0
4 Afterreduce=>{One=1,Two=2,Three=3}

Vous aimerez peut-être aussi