Using HashMap to implement concurrency

Source: Internet
Author: User

Admit that some of the title party tastes, but it is used in the actual asynchronous framework.

3-4 faster performance and lower CPU occupancy compared to the "accepted" concurrenthashmap approach

Demand

The asynchronous framework requires a buffer that holds the request data and is shared over multiple threads.

This is obviously a multithreaded concurrency problem.

Synchronous Lock Scheme

began to underestimate the problem, thought that simply lock resources, insert Request object, are memory operations, time is short, even if "blocking" is not serious.

Private  voidMultithreadsynclock (Final intNumofthread,Finalmap<string,string> map)throwsException {Final Long[] errcount=New Long[Numofthread+1]; Thread T=NewThread (NewRunnable () { Public voidrun () { for(inti = 0; i < Numofthread; i++) {                    NewThread (NewRunnable () { Public voidrun () {String Val=Uuid.randomuuid (). toString (); String Key=Thread.CurrentThread (). GetName (); intIndex=integer.parseint (key.substring (7, Key.length ())) +1; Longt1=System.currenttimemillis ();  for(intj=0;j<10000;j++) {                                synchronized(map) {map.put (key,val);}//Insert after acquiring lock                                if(!(val). Equals (Map.get (key))) errcount[0]++;//ErrCount >1 means reading data and writing different                            }                            LongT2=System.currenttimemillis (); Errcount[index]=+errcount[index]+t2-T1; }                    }, "Thread-" +i). Start (); }            }        }, "Yhread-main");        T.start (); Thread.CurrentThread (). Sleep (1000);        T.join (); LongTt=0;  for(inti=1;i<=numofthread;i++) tt=tt+Errcount[i]; Log.debug ("numofthread={},10,000 per thread,total time spent={}", NUMOFTHREAD,TT); Assert.assertequals (0,errcount[0]); }  
Synchronous lock Test Code

The results are appalling! And as the number of concurrent threads increases, "plugging" is serious

concurrent, each thread requests a plug-in
Data 10000 times, total time consuming
200 concurrent, per-thread request plug-in
Data 10000 times, total time consuming
4567.3ms 20423.95ms

Spin lock
@Test Public voidMULTITHREADPUTCONCURRENTHASHMAP100 ()throwsexception{FinalMap<string,string> map1=NewConcurrenthashmap<string,string> (512);  for(inti=0;i<100;i++) Multithreadputmap (100, MAP1); }    Private voidMultithreadputmap (Final intNumofthread,Finalmap<string,string> map)throwsException {Final Long[] errcount=New Long[Numofthread+1]; Thread T=NewThread (NewRunnable () { Public voidrun () { for(inti = 0; i < Numofthread; i++) {                    NewThread (NewRunnable () { Public voidrun () {String Val=Uuid.randomuuid (). toString (); String Key=Thread.CurrentThread (). GetName (); intIndex=integer.parseint (key.substring (7, Key.length ())) +1; Longt1=System.currenttimemillis ();  for(intj=0;j<10000;j++) {map.put (key,val);//the implementation of map Concurrenthashmap and HashMap                                if(!(val). Equals (Map.get (key))) errcount[0]++;//ErrCount >1 means reading data and writing different                            }                            LongT2=System.currenttimemillis (); Errcount[index]=+errcount[index]+t2-T1; }                    }, "Thread-" +i). Start (); }            }        }, "Yhread-main");        T.start (); Thread.CurrentThread (). Sleep (1000);        T.join (); LongTt=0;  for(inti=1;i<=numofthread;i++) tt=tt+Errcount[i]; Log.debug ("numofthread={},10,000 per thread,total time spent={}", NUMOFTHREAD,TT); Assert.assertequals (0,errcount[0]); }  
Spin lock test code
Using Concurrenthashmap
concurrent , each thread requests an insert
Data 10000 times, time consuming
Using Concurrenthashmap
Each thread requests an insert.
Data 10000 times, time consuming
Using Concurrenthashmap
concurrent , each thread requests an insert
Data 10000 times, time consuming
200.69ms 402.36ms 542.08ms

Compared with the synchronous lock, the efficiency is improved a lot, about 22-50 times, an order of magnitude gap!

Spin lock, the thread has been running, to avoid congestion, wake-to-back switching overhead, and a critical state of an instruction to complete, greatly improving efficiency.

Can it be further optimized?

As we all know, HASHMAP data structure is an array + linked list (refer to the online HashMap source analysis).
In simple terms, each time you insert data:

    1. Converts the given key to the array pointer p
    2. If ARRAY[P] is empty, save the Vlaue to Array[p]=value and complete the insert
    3. If ARRAY[P] is not empty, create a linked list, insert two objects, and save the linked list to array[p].
    4. When the fill rate is to the default of 0.75, it causes expansion.
Javadoc explicitly states that HashMap is not thread safe.but to be precise, the insecurity lies in:1, key repeat, linked list inserted. 2, expansion, filling rate of less than 0.75 in the guarantee key is not repeated, it should be the hash of key is not repeated, while the filling rate is less than 0.75 cases, multi-threaded insert/Read security . Therefore, can be further optimized, using the thread name as key, the request data, inserted into the hashmap. Avoid the lock!

Of course someone would ask

      1. is the thread name unique?
      2. How to guarantee the different thread name hash, corresponding to different pointers?
      3. Does the line routines insert multiple data?
      4. HashMap what happens to the expansion?
      5. Is performance improved?

Before answering, look at how Tomcat handles the request: When the request is reached, Tomcat pulls the idle thread from the thread pool, executes the filter, and the last servlet.

The Tomcat processing request has the following characteristics:

      • Process thread, cannot process new request until result is returned
      • The processing thread pool is usually small, about 200-300. (now more inclined to use multiple "small" size tomcat instances)
      • Thread name Unique

So

Question 1, 3 is obviously OK.

Issue 4, when initializing Hasmap, allocate a large space beforehand to avoid the expansion. For example, for 300 concurrency, New HASHMAP (512).

Although there are many requests, there are only 300 threads.

Problem 2,java is optimized for hash, which ensures the uniformity of hash and avoids repetition.

 Public voidCheckhashcodespreadoutenough () {intlength=512;  for(intj=0;j<10000;j++) {//Repeat 1000 times,Map<string,object> map=NewHashmap<string,object>(length);  for(inti=0;i<300;i++) {String key= "thread-" + (i+1); intHashcode=hash (key,length); Integer Keyhashcode=NewInteger (hashcode); Log.debug ("Key={} hashcode={}", Key,hashcode); if(Map.containskey (Keyhashcode)) {//The generated hash value is saved as a key in the hash table, as long as the duplicate indicates a conflictLog.error ("Encounter collisions! key={} hashcode={} ", Key,hashcode); Assert.asserttrue ("Encounter Collisions!",false); }            }        }    }    /** Calculation method of hash value extracted from HashMap source * Tracking code is generally not used Sun.misc.Hashing.stringHash32 ((String) k) calculation * About StringHash32, net     Have comments and are interested to check. */    Private intHash (Object K,intlength) {        intH = 0; H^=K.hashcode (); H^= (H >>>) ^ (H >>> 12); H=h ^ (h >>> 7) ^ (H >>> 4); returnH & (length-1); }  
The code above, hash the 300 thread names, detect conflicts, and run 10,000 times repeatedly. The result shows that there is no conflict.    In other words HashMap hash algorithm uniformity is not a problem, especially in this case environment, can guarantee the hash unique! Problem 5, performance issues, see Unit test results
Publiwuwu void throws exception{        final map<string,string> map=new// replacement Map implemented as HashMap         for (int i=0;i<100;i++)            multithreadputmap (map);    }   //  Multithreadputmap (100,map); see Concurrenthashmap Unit Test Code
hashmap no lock concurrency
use hashmap
100 concurrency per thread request Insert
data 10000 times, time consuming
use hashmap
200 concurrency , per thread request insert
data 10000 times, time consuming
use hashmap
300 concurrency Span class= "Font3" >, per thread request Insert
data 10000 times, time consuming
46.79ms 99.42ms 137.03
increase 4.289164351 times times increase 4.04707 3,024 times times increase by 3.955922061 times times

There are nearly 3-4 times the increase

Conclusion
    • Under certain circumstances, HashMap can be used in multi-threaded concurrency environment
    • A sync lock, which belongs to a sleep-waiting type of lock. The state changes, causing the CPU to switch back and forth. thus inefficient.
    • Spin lock, always occupy the CPU, keep trying until success. Sometimes the single-core situation causes "suspended animation"
    • Careful, analysis, can find no "lock" way to solve multi-threaded concurrency problems, to achieve higher performance and smaller costs

Other

In i5-2.5g,8g win10 jdk1.7.0_17,64bit

Test data, not considering garbage collection, the data some fluctuations.

HashMap The default fill factor is 0.75, which is modified in the construction method.

Using HashMap to implement concurrency

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.