The new lock class improves synchronization-but synchronized jdk5 cannot be discarded now

Source: Internet
Author: User
Tags benchmark
Content:
Synchronized quick review
Improvement on synchronized
Compare the scalability of reentrantlock and synchronized
Condition variable
Unfair
Conclusion
References
About the author
Comments on this article
Related content:
Java Theory and PracticeSeries
Synchronization is not the enemy
Indexing contention
IBM developer kits for the Java platform (downloads)
Subscription:
Developerworks Newsletters
Developerworks subscription
(Subscribe to Cd and download)

The new lock class improves synchronization-but synchronized cannot be discarded now

Level: Intermediate

Brian Goetz (brian@quiotix.com)
Chief Consultant, quiotix
November 2004

JDK 5.0 provides some effective new options for developers to develop high-performance concurrent applications. For example,java.util.concurrent.lockClassReentrantLockBeing used as a Java languagesynchronizedFunction substitution, it has the same memory semantics, the same lock, but has better performance under the contention condition, in addition, it also hassynchronizedOther features are not provided. Does this mean we should forgetsynchronizedInstead, onlyReentrantLockWhat about it? Concurrency expert Brian Goetz just returned from his summer vacation and will give us the answer.

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 containsThreadClass, which can be used to construct, start, and manipulate threads. the Java language includes a structure for cross-thread transmission of concurrency constraints --synchronizedAndvolatile. 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 abnormal behaviors of memory cache and compiler optimization. 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 through command shuffling or other compiler optimizations ), it is not subject to cached variable values, but if developers use synchronization, as shown in the following code, the Runtime Library will ensure that a thread updates the variable prior to the existingsynchronizedWhen 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 involatileVariable. (For details about the synchronization and Java memory models, see references .)

synchronized (lockObject) {   // update object state}

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 ), in addition, ensure that the latest values of these variables are visible to other threads correctly synchronized. 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 isLockMultiple implementations leave space. Different implementations may have different scheduling algorithms, performance characteristics, or locking semantics.ReentrantLockClassLock, 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 imitatessynchronizedIf 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.

Lock lock = new ReentrantLock();lock.lock();try {   // update object state}finally {  lock.unlock(); }

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 lockReentrantLockGenerallysynchronizedMuch 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 measuresynchronizedAndLock. This example is good, because each callnextRandom()PRNG is doing some work, so this benchmark program is actually measuring a reasonable and truesynchronizedAndLockApplications, 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 correspondsjava.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 usesjava.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 showsynchronizedAndReentrantLockCompared with the scalability.

Figure 1. synchronized and lock throughput, single CPU

Figure 2. synchronized and lock throughput (after standardization), 4 CPUs

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, whileLockThe 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()AndnotifyAll()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 introducesjava.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 likeLockIs synchronized In summary,LockThe framework includeswaitAndnotifyIs calledCondition).LockThe object acts as the factory object bound to the condition variable bound to this lock, with the standardwaitAndnotifyDifferent 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 chooseFair (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 setfalseUnless 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 defaultReentrantLockIt is "unfair". This fact is just a superficial process of synchronization. If you do not mind this during synchronizationReentrantLockDo 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.

Figure 3. Relative throughput of synchronization, negotiation, and fair locks when four CPUs are used

Figure 4. Relative throughput of synchronization, negotiation, and fair lock when one CPU is used

Good everywhere?
Looks likeReentrantLockBothsynchronizedGood -- allsynchronizedWhat you can do, it can do, it hassynchronizedWith the same memory and concurrency semanticssynchronizedFeatures 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 useReentrantLockRewrite our existingsynchronizedCode? In fact, several introductory Java programming books use this method in their multi-threaded chapter, fully usedLockFor 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 youLockThere 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 thatReentrantLock"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.

Conclusion
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 neededLockIt is used. In these cases, you will be happy to have this tool.

References

  • For more information, see the original article on the developerworks global site.

  • Please read the complete article by Brian GoetzJava Theory and PracticeSeries.
  • Developers often confuse non-competing synchronization costs with competing synchronization costs. "Synchronization is not the enemy "(Developerworks, July 2001) provided some rough benchmark checks to estimate the cost of non-competing synchronization "(DeveloperworksSeptember 2001) provides tutorials to reduce the impact of lock contention in applications.
  • Lock's javadoc, reentrantlock, and condition provide more information about the application and behavior of the new lock class.
  • Written by Doug LeaConcurrent Programming in Java, Second EditionIs an authoritative book on some subtle issues in multi-thread programming in Java programming.
  • The demonstration document of javaone in 2004 summarizes some concurrency improvements in JDK 5.0.
  • InDeveloperworksIn the Java technology area, you can find articles in various aspects of Java.
  • See developer bookstore for a complete list of technical books, including hundreds of books on Java-related topics.

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.