0% found this document useful (0 votes)
69 views

Concurrenthashmap in Java 8: Parallelismthreshold

The document discusses new features introduced in Java 8 for ConcurrentHashMap, including parallel bulk operations like forEach, search, and reduce. It describes how these operations work by using ForkJoinTasks to parallelize work across threads based on a parallelismThreshold. The key-value mapping methods like forEach, search, and merge are implemented using ForkJoinTasks like ForEachMappingTask and SearchMappingsTask that extend BulkTask and ForkJoinTask to parallelize the work.

Uploaded by

Jivtesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views

Concurrenthashmap in Java 8: Parallelismthreshold

The document discusses new features introduced in Java 8 for ConcurrentHashMap, including parallel bulk operations like forEach, search, and reduce. It describes how these operations work by using ForkJoinTasks to parallelize work across threads based on a parallelismThreshold. The key-value mapping methods like forEach, search, and merge are implemented using ForkJoinTasks like ForEachMappingTask and SearchMappingsTask that extend BulkTask and ForkJoinTask to parallelize the work.

Uploaded by

Jivtesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 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}

You might also like