Lock optimization and Precautions

Source: Internet
Author: User

Lock optimization divided into code-level optimization and JVM-level optimization 1. Code-level lock optimization ideas and methods

Once the lock is used, it means that it is blocked, so the concurrency is generally lower than the lock-free situation.

The lock optimization mentioned here refers to how the performance does not get too bad in the case of blocking. But how to optimize, in general, performance is more than the lock-free situation is almost.

The Trylock in Reentrantlock, in favor of a lock-free way, because in trylock judgment, does not hang himself.

Lock optimization ideas and methods summarized, there are several.

    • Reduced lock holding time

    • Reduce lock granularity

    • Lock separation

    • Lock coarsening

    • Lock removal

1.1 Reduction of lock holding time
 Public synchronized void Syncmethod () {          othercode1 ();          Mutextmethod ();          Othercode2 ();     }

Like the code above, you get a lock before you enter the method, and the other threads wait outside.

One of the optimizations here is to reduce the time that other threads wait, so lock only on programs that are thread-safe.

 Public void Syncmethod () {          othercode1 ();           synchronized (this)        {            mutextmethod ();          }        Othercode2 ();     }

1.2 Reduction of lock granularity

Large objects (which may be accessed by many threads) are split into small objects, greatly increasing the degree of parallelism and reducing lock contention. Reduces the lock competition, the bias lock, the lightweight lock success rate will improve.

The most typical case of reducing the granularity of lock is concurrenthashmap.

1.3 Lock Separation

The most common lock separation is read-write lock Readwritelock, according to the function to separate into read lock and write lock, so that read-read is not mutually exclusive, read-write mutex, write mutually exclusive, that is, to ensure the thread safety, but also improve the performance.

Reading and writing separation thought can extend, as long as the operation does not affect each other, the lock can be separated.

For example, Linkedblockingqueue from the head, and data from the tail. The JDK and the contract 2 mentioned in the Forkjoinpool in the work stealing.

1.4 Lock Coarsening

In general, in order to ensure effective concurrency between multiple threads, each thread is required to hold the lock for as short a time as possible, that is, the lock should be released immediately after using the public resources. This is the only way to wait for other threads in the lock to get resources to perform the task as early as possible. However, all things have a degree, if the same lock on the continuous request, synchronization and release, it will also consume the system's valuable resources, but it is not conducive to performance optimization.

As an example:

 Public void DemoMethod () {          synchronized(lock) {               //dosth.           }          // do other work  that does not need synchronization, but can be executed very quickly        synchronized( Lock) {               //dosth.           }     }

This situation, according to the idea of lock coarsening, should be merged

 Public void DemoMethod () {          //        synchronized(lock) {                // Do sth.                // do other tasks that you don't need to sync, but you can do  it quickly         }    }

Of course, this is a prerequisite, the premise is that those who do not need synchronization in the middle of the work is done quickly.

To cite an extreme example:

 for (int i=0;i<circle;i++) {     synchronized(lock) {     }}

It is different to get a lock within a loop. Although the JDK will do some optimizations for this code, it might as well be written directly

synchronized (lock) {     for (int i=0;i<circle;i++) {     }} 

Of course, if there is a need to say that such a cycle too long, you need to give other threads do not wait too long, it can only be written in the above. If there is no such a requirement, it is better to write it directly below.

1.5 Lock Removal

Lock elimination is something at the compiler level.

In an instant compiler, if you find objects that cannot be shared, you can eliminate the lock operations of those objects.

Perhaps you may find it strange that since some objects cannot be accessed by multiple threads, why should they be locked? It is better to write code without locking it directly.

But sometimes, these locks are not written by the programmer, there are locks in the JDK implementations, such as vectors and StringBuffer, and many of the methods are locked. When we use the methods of these classes in some cases where it is not safe to thread, the compiler will eliminate lock elimination to improve performance when certain conditions are met.

Like what:

 Public Static voidMain (String args[])throwsinterruptedexception {LongStart =System.currenttimemillis ();  for(inti = 0; i < 2000000; i++) {Createstringbuffer ("JVM", "diagnosis"); }        LongBuffercost = System.currenttimemillis ()-start; System.out.println ("Craetestringbuffer:" + buffercost + "MS"); }      Public Staticstring Createstringbuffer (string s1, string s2) {StringBuffer SB=NewStringBuffer ();        Sb.append (S1);        Sb.append (S2); returnsb.tostring (); }

The stringbuffer.append in the code above is a synchronous operation, but StringBuffer is a local variable, and the method does not return the StringBuffer, so it is not possible to have multiple threads to access it.

Then the synchronous operation in StringBuffer is meaningless at this point.

Open lock elimination is set on the JVM parameters and of course needs to be in server mode:

-server-xx:+doescapeanalysis-xx:+eliminatelocks

And to open the escape analysis. The function of escape analysis is to see if variables are likely to escape the scope of the scope.

For example, the above-mentioned StringBuffer, the return of Craetestringbuffer in the above code is a string, so this local variable stringbuffer will not be used anywhere else. If you change the Craetestringbuffer to

 Public Static stringbuffer Craetestringbuffer (string s1, string s2) {        new  stringbuffer ();        Sb.append (S1);        Sb.append (S2);         return sb;    }

Then this stringbuffer is returned, it is possible to be used by any other place (for example, the main function will return the result put into the map Ah, etc.). Then the JVM escape analysis can be analyzed, this local variable StringBuffer escaped its scope.

So, based on the escape analysis, the JVM can determine that if the local variable StringBuffer does not escape its scope, then it can be determined that the stringbuffer is not accessed by multithreading, then you can remove these extra locks to improve performance.

When the JVM parameter is:

-SERVER-XX:+DOESCAPEANALYSIS-XX:+eliminatelocks

Output:

craetestringbuffer:302 ms

The JVM parameters are:

-SERVER-XX:+DOESCAPEANALYSIS-XX:-eliminatelocks

Output:

craetestringbuffer:660 ms

Obviously, the effect of lock elimination is still very obvious.

2. Lock optimization within a virtual machine

In the JVM, each object has an object header.

Mark Word, tag for object header, 32-bit (32-bit system).

Describes the object's hash, lock information, garbage collection token, age also holds a pointer to the lock record, pointer to monitor, favor lock thread ID, and so on.

Simply put, the object header is to save some systematic information.

2.1 bias Lock

The so-called bias, is the eccentricity, that is, the lock will be biased towards the current lock thread.

Most of the time there is no competition (in most cases a synchronization block will not have multi-threaded simultaneous competition locks), so you can improve performance by bias. That is, in the absence of competition, the thread that acquired the lock once again obtains the lock, will determine whether to bias the lock to point to me, then the thread will not have to obtain the lock again, can directly enter the synchronization block.

The implementation of a bias lock is to set the tag of the object head mark to bias and write the thread ID to the object header mark

Bias mode ends when other threads request the same lock

JVM default enable biased lock-xx:+usebiasedlocking

In highly competitive situations, biased locking increases the burden on the system (each time a bias is added)

Example of favor Lock:

 Public classTest { Public StaticList<integer> numberlist =NewVector<integer>();  Public Static voidMain (string[] args)throwsinterruptedexception {LongBegin =System.currenttimemillis (); intCount = 0; intStartnum = 0;  while(Count < 10000000) {numberlist.add (startnum); Startnum+ = 2; Count++; }        LongEnd =System.currenttimemillis (); System.out.println (End-begin); } }

Vector is a thread-safe class that uses a lock mechanism internally. Each add locks the request. The code above has only one thread of main and then repeatedly add request lock.

Use the following JVM parameters to set the bias lock:

-xx:+usebiasedlocking-xx:biasedlockingstartupdelay=0

Biasedlockingstartupdelay indicates that a biased lock is enabled after a few seconds of system boot. The default is 4 seconds, because when the system starts, the general data race is more intense, and enabling biased locking can degrade performance.

Since this is to test the performance of a biased lock, the delay-biased lock time is set to 0.

This time the output is 9209

Turn off the bias lock below:

-xx:-usebiasedlocking

Output is 9627

Generally, when there is no competition, the ability to turn on biased locking increases by around 5%.

2.2 Lightweight Lock

The multithreading security of Java is implemented based on the lock mechanism, and the performance of lock is often unsatisfactory.

The reason is thatmonitorenter and Monitorexit, the two bytecode primitives that control multithreading synchronization, are implemented by the JVM relying on operating system mutexes (mutexes).

A mutex is a resource-intensive operation that causes a thread to hang and, in a short time, needs to be re-dispatched back to the original thread.

In order to optimize the lock mechanism of Java, the concept of lightweight locking was introduced from JAVA6.

The lightweight lock (lightweight Locking) is intended to reduce the chance of multiple threads entering the mutex, and not to replace the mutex.

It uses the CPU primitive Compare-and-swap (CAS, assembly instruction Cmpxchg) to try to remediate before entering the mutex.

If the bias lock fails, the system will perform a lightweight lock operation. Its purpose is to minimize the need to use the mutex at the operating system level, because that performance will be poor. Because the JVM itself is an application, it is desirable to resolve thread synchronization issues at the application level.

To summarize, the lightweight lock is a fast locking method that, before entering the mutex, uses CAS operations to try to lock and try not to use the mutex at the operating system level to improve performance.

So when the bias lock fails, the lightweight lock steps:

1. Save the mark pointer of the object's head to the lock object (the object here refers to the locked object, such as synchronized (this) {},this is the object here).

Lock->set_displaced_header (Mark);

2. Set the object header to a pointer to the lock (in the online stacks space).

if (Mark = = (markoop) atomic::cmpxchg_ptr (lock, obj (), mark_addr (), Mark)) {            tevent (slow_enter:release Stacklock);             return  ; }

Lock is located in the line stacks. So judging whether a thread holds this lock, just determine whether the space that the object's head points to is in the address space of the line stacks.

If the lightweight lock fails, it means there is a competition, and upgrading to a heavyweight lock (regular lock) is the synchronization method at the operating system level. In the absence of lock contention, lightweight locks reduce the performance loss that traditional locks incur by using OS mutexes. When competition is intense (lightweight locks always fail), lightweight locks do a lot of extra work, resulting in degraded performance.

2.3 Spin Lock

When the competition exists, because the lightweight lock attempt fails, it is possible to escalate directly into a heavyweight lock to use the mutex at the operating system level. It is also possible to try a spin lock again.

If a thread can get a lock quickly, it can not suspend the thread at the OS layer, let the thread do a few empty operations (spin), and keep trying to get the lock (similar to Trylock), of course, the number of cycles is limited, when the number of cycles reached, still upgraded to a heavyweight lock. So when each thread has little hold of the lock, the spin lock tries to avoid the thread being suspended at the OS level.

JDK1.6 in-xx:+usespinning Open

JDK1.7, remove this parameter and change the built-in implementation

If the synchronization block is long, spin failures can degrade system performance. If the synchronization block is short, spin is successful, saving thread suspend switching time and improving system performance.

2.4 bias Lock, lightweight lock, spin lock summary

First the lock is biased to avoid the performance of a thread repeatedly acquiring/releasing the same lock, if it is still the same thread to obtain the lock, try to bias the lock will be directly into the synchronization block, do not need to obtain a lock again.

Both lightweight and spin locks are designed to avoid direct invocation of mutex operations at the operating system level, because suspending a thread is a resource-intensive operation.

To avoid the use of a heavyweight lock (an operating system-level mutex), the lightweight lock is tried first, and the lightweight lock tries to obtain the lock using a CAS operation, which means there is a competition if the lightweight lock fails. But it may soon be possible to get a lock, try a spin lock, make a few empty loops of the thread, and keep trying to get the lock every time you loop. If the spin lock also fails, it can only be upgraded to a heavyweight lock.

Visible bias Lock, lightweight lock, spin lock are optimistic lock.

 

Lock optimization and Precautions

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.