Comparison between reentrantlock and synchronized in Java

Source: Internet
Author: User
Tags benchmark finally block

Multithreading and concurrency are not new content, but one of the innovations in Java language design is that it is the first mainstream language to directly integrate the cross-platform thread model and regular memory model into the language. The core class library contains
ThreadClass, which can be used to construct, start, and manipulate threads. the Java language includes a structure for cross-thread transmission of concurrency constraints --synchronizedAnd
volatile. While simplifying the development of concurrent classes unrelated to the platform, it never makes the writing of concurrent classes more complicated, but makes it easier.

Synchronized quick review

Declaring a code block as synchronized has two important consequences:Atomicity)AndVisibility). Atomicity means that a thread can only execute code protected by a specified Monitoring object (LOCK) at a time, so as to prevent multiple threads from conflicting with each other when updating the sharing status. Visibility is more subtle; it has to deal with internal memory cache and compiler-optimized abnormal behavior. Generally, threads are in a way that does not need to be immediately visible to other threads (whether these threads are in registers, in a processor-specific cache, or by means of rescheduling or other compiler optimizations ), it is not restricted by the cached variable value, but if developers use synchronization, as shown in the following code, the Runtime Library will ensure that a thread updates the variable before the current
YessynchronizedWhen a block is updated, it enters another one protected by the same Monitor (Lock ).synchronizedYou can immediately see the updates to the variables. Similar rules exist in
volatileVariable.

[Java]
View plaincopy
  1. Synchronized (lockobject ){
  2. // Update object state
  3. }

Therefore, to implement synchronization, you need to consider everything required to securely update multiple shared variables. There are no contention conditions and data cannot be damaged (assuming that the synchronized boundary location is correct ), make sure that other threads that are correctly synchronized can view the latest values of these variables. By defining a clear and cross-platform Memory Model (this model was modified in JDK 5.0 to correct some errors in the original definition), by following the simple rule below, it is possible to build a "write once, run anywhere" concurrent class:

At any time, as long as the variable you write may be read by another thread, or the variable you read is finally written by another thread, you must synchronize it.

But now, in the recent JVM, there is no contention for synchronization (when a thread has a lock, no other threads attempt to obtain the lock). The performance cost is still very low. (This is not always the case; synchronization in early JVMs has not been optimized, so many people think so, but now this has become a misunderstanding, people think that whether or not it is competing for use, synchronization has a high performance cost .)

Improvement on synchronized

It seems that synchronization is quite good, right? So why did the JSR 166 team spend so much time developing?java.util.concurrent.lockWhat about the framework? The answer is simple-synchronization is good, but it is not perfect. It has some functional limitations-it cannot interrupt a thread waiting to get the lock, nor get the lock by voting. If you don't want to wait, you won't be able to get the lock. Synchronization also requires that the lock can be released only in the same stack frame as the stack frame in which the lock is obtained. In most cases, this is okay (and it interacts well with Exception Handling ), however, some non-block structure locks are more appropriate.

Reentrantlock class

java.util.concurrent.lockInLockThe Framework is an abstraction of locking. It allows the implementation of locking as a Java class, rather than as a language feature. This is
LockMultiple implementations leave space. Different implementations may have different scheduling algorithms, performance characteristics, or locking semantics.ReentrantLockClass
Lock, It hassynchronizedWith the same concurrency and memory semantics, some features such as lock voting, timed lock wait, and stoppedlock wait are added. In addition, it provides better performance in the case of fierce competition. (In other words, when many threads want to access shared resources, JVM can spend less time scheduling threads and spend more time on execution threads .)

ReentrantWhat does a lock mean? To put it simply, there is a lock-related acquisition counter. If a thread that owns the lock gets the lock again, it will add 1 to the acquisition counter, then the lock must be released twice before it can be truly released. This imitates
synchronizedIf the thread enters the synchronized block protected by the monitor already owned by the thread, the thread is allowed to continue. When the thread exits the second (or later)
synchronizedBlock, do not release the lock, only the thread exits it into the monitor to protect the firstsynchronizedBlock to release the lock.

When viewing the sample code in Listing 1, you can seeLockThere is a major difference from synchronized-lock must be released in the Finally block. Otherwise, if the protected code throws an exception, the lock may never be released! This difference may seem insignificant, but in fact it is extremely important. If you forget to release the lock in the Finally block, a timer bomb may be left in the program. When one day the bomb explodes, it takes a lot of effort to find the source. With synchronization, JVM ensures that the lock is automatically released.

Listing 1. Use reentrantlock to protect code blocks.

[Java]
View plaincopy
  1. Lock = new reentrantlock ();
  2. Lock. Lock ();
  3. Try {
  4. // Update object state
  5. }
  6. Finally {
  7. Lock. Unlock ();
  8. }

In addition, compared with the current synchronized implementationReentrantLockAchieve more scalability. (In future JVM versions, the competition performance of synchronized is likely to be improved .) This means that when multiple threads compete for the same lock
ReentrantLockGenerallysynchronizedMuch less.

Compare the scalability of reentrantlock and synchronized

Tim Peierls uses a simple linear fully-equal pseudo-random number generator (PRNG) to construct a simple evaluation and use it to measuresynchronizedAnd
Lock. This example is good, because each callnextRandom()PRNG is doing some work, so this benchmark program is actually measuring a reasonable and true
synchronizedAndLockApplications, rather than testing purely on paper or code that does nothing (just like many so-called benchmarking programs .)

In this benchmark program, there isPseudoRandomInterface, which has only one methodnextRandom(int bound). This interface corresponds
java.util.RandomClass functions are very similar. When the next random number is generated, PRNG uses the latest number as the input, and maintains the last number as an instance variable, the focus is to prevent code segments updated in this state from being preemptible by other threads, so I will use some form of locking to ensure this. (
java.util.RandomClass .) ForPseudoRandomTwo implementations are built. One uses syncronized and the other uses
java.util.concurrent.ReentrantLock. The driver generates a large number of threads, and each thread is frantically competing for a time slice, and then calculating the number of rounds that different versions can execute per second. Figures 1 and 2 summarize the results of different threads. This evaluation is not perfect, and only runs on two systems (one is dual Xeon running hyper-threading Linux, and the other is a single processor Windows System), but should be sufficient to show
synchronizedAndReentrantLockCompared with the scalability.

The charts in Figure 1 and figure 2 show the throughput in units of calls per second. Different implementations are adjusted to 1 thread.synchronized. Each implementation is relatively quickly concentrated on the throughput of a stable State. This State usually requires the processor to be fully utilized, and most of the processor's time is spent in actual work (computer random number) only a small amount of time is spent on Thread Scheduling expenses. You will notice that the synchronized version performs quite poorly when processing any type of contention, while
LockThe version takes a relatively small amount of time on scheduling expenses, thus leaving space for higher throughput and achieving more effective CPU utilization.

Condition variable

Root classObjectContains some special methods used in the threadwait(),notify()And
notifyAll()Communication. These are advanced concurrency features that many developers have never used-this may be a good thing because they are quite subtle and easy to use improperly. Fortunately, as JDK 5.0 introduces
java.util.concurrentThere is almost no need for developers to use these methods.

There is an interaction between the notification and the lock-to be on the objectwaitOrnotifyYou must hold the lock for this object. Just like
Lock
Is synchronized In summary,LockThe framework includeswaitAndnotifyIs called
Condition).LockThe object acts as the factory object bound to the condition variable bound to this lock, with the standardwaitAnd
notifyDifferent methods, for the specifiedLock, There can be more than one conditional variable associated with it. This simplifies the development of many concurrent algorithms. For example,
Condition)Javadoc of shows an example of bounded buffer implementation. This example uses two conditional variables: "Not full" and "not empty ", it is more readable (and more effective) than only one wait setting for each lock ).
ConditionMethods andwait,notifyAndnotifyAllThe method is similar.
await,signalAndsignalAllBecause they cannot be overwritten.
Object
.

Unfair

If you view javadoc, you will see,ReentrantLockA parameter of the constructor is a Boolean value, which allows you to choose
Fair (fair)
Lock, orUnfair (unfair)Lock. Fair locks allow the thread to obtain locks in order of request locks, while unfair locks allow bargaining. In this case, the thread can sometimes obtain locks first than other threads that request locks first.

Why don't we make all the locks fair? After all, fairness is a good thing, but it is not good to be unfair, isn't it? (When children want to make a decision, they always yell "this is not fair ". We think fairness is very important, and the children know it .) In reality, fairness ensures that the lock is very robust and has a high performance cost. To ensure the bookkeeping and synchronization required for fairness, it means that the competing fair locks have lower throughput than the unfair locks. As the default setting, fairness should be set
falseUnless fairness is critical to your algorithm, it must be served strictly in the order of thread queuing.

What about synchronization? Is the built-in monitor lock fair? The answer surprised many people. They are unfair and never unfair. But no one complained about thread hunger, because JVM ensures that all threads will eventually get the lock they are waiting. Ensuring the fairness of statistics is sufficient in most cases, and the cost is much lower than the absolute fairness guarantee. Therefore, by default
ReentrantLockIt is "unfair". This fact is just a superficial process of synchronization. If you do not mind this during synchronization
ReentrantLock
Do not worry about it.

Figure 3 and figure 4 contain the same data as Figure 1 and figure 2. They only add a dataset for random number benchmark detection. This detection uses a fair lock instead of the default negotiated lock. As you can see, fairness has a price. If you need to be fair, you must pay the price, but do not use it as your default choice.

Good everywhere?

Looks likeReentrantLockBothsynchronizedGood -- all
synchronized
What you can do, it can do, it hassynchronizedWith the same memory and concurrency semantics
synchronized
Features that are not available, but also have better performance under load. So should we forgetsynchronizedAnd no longer regard it as a good idea that has been optimized? Or even use
ReentrantLockRewrite our existingsynchronizedCode? In fact, several introductory Java programming books use this method in their multi-threaded chapter, fully used
LockFor example, we only use synchronized as the history. But I think this is too good.

Do not discard synchronized

AlthoughReentrantLockIt is a very touching implementation. Synchronized has some important advantages, but I think it is definitely a serious mistake to rush to regard synchronized as just too busy.
java.util.concurrent.lock The lock class in is a tool used for advanced users and advanced situations.. In general, unless you
Lock
There is a clear need for a certain advanced feature of, or there is clear evidence (rather than just doubt) that under certain circumstances, synchronization has become a bottleneck in scalability, otherwise synchronized should still be used.

Why is my opinion conservative in the use of an apparently "better" implementation? Becausejava.util.concurrent.lockFor the lock class in, synchronized still has some advantages. For example, when synchronized is used, you cannot forget to release the lock.
synchronizedThe JVM will do this for you. You can easily forget to usefinallyBlock release lock, which is very harmful to the program. Your program can pass the test, but there will be deadlocks in the actual work, it will be difficult to identify the cause (this is why it is not intended for junior developers at all)
Lock.)

Another reason is that when the JVM uses synchronized to manage lock requests and releases, the JVM can include lock information when generating thread dump. These are very valuable for debugging because they can identify the sources of deadlocks or other abnormal behaviors.
LockThe class is just a common class, And the JVM does not know which thread hasLockObject. In addition, almost every developer is familiar with synchronized, which can work in all versions of JVM. Before JDK 5.0 becomes a standard (it may take two years from now on ),
LockClass will mean that not every JVM has the features to be used, and not every developer is familiar with them.

When should I replace synchronized with reentrantlock?

In this case, when should we useReentrantLockWhat about it? The answer is very simple-when some features not available for synchronized are required, such as waiting for a time lock, waiting for an interrupted lock, no block structure lock, multiple conditional variables, or lock voting.
ReentrantLockIt also has the scalability benefit and should be used in highly competitive situations, but remember that most synchronized blocks have never been competing, so we can put high contention on one side. I suggest using synchronized for development until it does prove that synchronized is inappropriate, instead of simply assuming that
ReentrantLock"Better performance ". Remember that these are advanced tools for advanced users. (Moreover, real advanced users prefer to select the simplest tool they can find until they think that simple tools are not applicable .). As always, we should first do a good job and then consider whether it is necessary to do it faster.

LockThe framework is a substitute for synchronization compatibility.synchronizedMany features are not provided, and its implementation provides better performance in competition. However, these obvious advantages are not enough for use.
ReentrantLockReplacesynchronized. On the contraryYes
ReentrantLockAbility to make a choice. In most cases, you should not select it -- synchronized works well and can work on all JVMs. More developers know about it and it is not easy to make mistakes. Only when needed
LockIt is used. In these cases, you will be happy to have this tool.

Related Article

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.