|
I would like to clear up the fair amount of misconception that remains about the reader/writer lock. The name implies the presence of two distinct locks -- one for reading and one for writing. However, that is not the case. A reader/writer lock is a single lock. Think of it as a lock with two levels, much like a building with two floors or a game with two levels -- beginner and advanced. We say that a set of processes or threads is deadlocked when each thread is waiting for an event that only another process in the set can cause. Another way to illustrate a deadlock is to build a directed graph whose vertices are threads or processes and whose edges represent the "is-waiting-for" relation. If this graph contains a cycle, the system is deadlocked. Unless the system is designed to recover from deadlocks, a deadlock causes the program or system to hang. In this chapter, we look at some of the more advanced issues related to data synchronization--specifically, timing issues related to data synchronization. When you write a Java program that makes use of several threads, issues related to data synchronization are those most likely to create difficulties in the design of the program, and errors in data synchronization are often the most difficult to detect since they depend on events happening in a specific order. Often an error in data synchronization can be masked in the code by timing dependencies. You may notice some sort of data corruption in a normal run of your program, but when you run the program in a debugger or add some debugging statements to the code, the timing of the program is completely changed, and the data synchronization error no longer occurs. Editor's note: Last week, in part one of this two-part excerpt from Java Threads, 3rd Edition, authors Scott Oaks and Henry Wong looked at the new java.util.concurrent package. This week, the authors conclude by looking at deadlock and how to deal with it. Java Tech: The ABCs of Synchronization, Part 1 Java's thread support is powerful and comprehensive, but it can also lead to problems if you don't fully understand what you're doing. In his latest Java Tech column, Jeff Friesen introduces the concepts of locks, synchronization, and the dangers of deadlock. Note: Java 1.5 introduces the java.util.concurrent.locks package. This package includes a Lock interface for implementing locking operations that are more extensive than those offered by synchronized methods and synchronized statements. One of the strengths of the Java programming language is its support for multithreading at the language level. Much of this support centers on synchronization: coordinating activities and data access among multiple threads. The mechanism that Java uses to support synchronization is the monitor. This chapter describes monitors and shows how they are used by the Java virtual machine. It describes how one aspect of monitors, the locking and unlocking of data, is supported in the instruction set. This month's Under The Hood looks at thread synchronization in both the Java language and the Java virtual machine (JVM). This article is the last in the long series of bytecode articles I began last summer. It describes the only two opcodes directly related to thread synchronization, the opcodes used for entering and exiting monitors. One way to avoid this problem is for code to acquire locks in a fixed, global order. In this example, if thread 1 and thread 2 call the sumArrays method with the parameters in the same order, the deadlock will not occur. This technique, however, requires programmers of multithreaded code to be careful in how they invoke methods that lock objects passed as parameters. Application of such a technique might seem unreasonable until you encounter this type of deadlock and have to debug it. For most programming languages, the language specification is silent on the topic of threading and concurrency; these topics have historically been left for the platform or operating system to specify. In contrast, the Java Language Specification (JLS) explicitly includes a threading model and provides several language elements for developers to use for rendering their programs thread-safe. As luck would have it, escape analysis, which we discussed last month, provides the compiler with exactly the information it needs to optimize away synchronized blocks that use thread-local lock objects. If the compiler can prove (using escape analysis) that an object is never published to the heap, it must be a thread-local object and therefore any synchronized blocks that use that object as a lock will have no effect under the Java Memory Model (JMM) and can be eliminated. This optimization is called lock elision and is another of the JVM optimizations slated for Mustang. The synchronized keyword locks objects. Because the object is locked inside of synchronized code, what does that mean to the object and to changes you make to its object reference? Synchronizing on an object locks only the object. You must be careful, however, not to reassign an object reference of a locked object. What happens if you do? Consider the following code that implements a stack: After publication of my October article, "More flexible, scalable locking in JDK 5.0," a colleague sent me the SyncLockTest benchmark (shown in Listing 1), which was purported to determine whether the synchronized primitive or the new ReentrantLock class was "faster." After running it on his laptop, he came to the conclusion that synchronization was faster, contrary to the conclusion of the article, and presented his benchmark as "evidence." The entire process -- the design of the microbenchmark, its implementation, its execution, and the interpretation of its results were flawed in a number ways. The colleague in this case is a pretty smart guy who's been around the block a few times, which goes to show how difficult this stuff can be. |
w__ww__.__ja_v__a___2__s._c___o___m___ | Contact Us |
Copyright 2009 - 12 Demo Source and Support. All rights reserved. |
All other trademarks are property of their respective owners. |