| By Craig Caulfield | Article Rating: |
|
| April 21, 2006 02:00 PM EDT | Reads: |
25,291 |
The only way to avoid deadlock is by preventing the occurrence of one of the three conditions of policy above (which is often beyond the developer's control) or by preventing the occurrence of a circular wait. The usual way of preventing a circular wait is to design a lock hierarchy and make sure that all threads acquire their locks in the same sequence.
As always, developers are solely responsible for preventing deadlock in their applications, but some of the new utilities in J2SE 5.0 can help with this. For example, a thread will block indefinitely when it tries to acquire a lock that's owned by another thread. An alternative is the lockInterruptibly() method from the Lock interface. If the interrupt() method is called on a thread while it's waiting to acquire a lock, an InterruptedException is thrown and the attempt to acquire the lock is abandoned. From here, one possible course of action would be to throw a fresh exception on the basis of which the program can decide what to do next in the absence of a lock. For example:
Lock lock = new ReentrantLock();
try {
lock.lockInterruptibly();
try {
// Lock acquired, so work with the object state as normal
} finally {lock.unlock(); }
} catch (InterruptedException e) {
// Can't acquire the lock. So end, or throw a new exception to let the caller know.
}
Rather than trying to acquire a lock, possibly waiting, and possibly needing to be interrupted, lock acquisition can also be more tentative:
Lock lock = new ReentrantLock();
if (lock.tryLock())
// Lock acquired, so work with the object state as normal
try { . . . }
finally { lock.unlock(); }
else
// Can't acquire the lock, so take another path
The tryLock() method tries to acquire a lock and will return true if it was successful, otherwise it immediately returns false. If a thread can't acquire a lock, some alternative execution path can be taken. This alternative path may use a physical resource or data structure that isn't quite as fast or efficient as the first choice, but at least the program is progressing rather than spinning its wheels.
The tryLock() method can also be called with a timeout parameter. For example:
if (lock.tryLock(100, TimeUnit.MILLISECONDS))...
Using tryLock() with a timeout parameter has other advantages. If tryLock() is called with a timeout parameter, an InterruptedException will be thrown if the thread is interrupted during the timeout period. The exception handling in this case might include releasing any resources held and trying some alternative execution path, making the chances of a deadlock occurring less likely.
The same sort of timeouts can also be specified when waiting on conditions.
condition.await(100, TimeUnit.MILLISECONDS))
In common with tryLock(), the await() method returns if the timeout has elapsed or if the thread is interrupted, and in the normal cases where another thread calls the signalAll() or signal() methods.
Lock testing and timeouts give developers more options for keeping their applications out of deadlock, but this comes at a small cost. Preventing circular waits using a lock hierarchy simply means working out the order in which threads should acquire their locks. Meanwhile, to use lock testing and timeouts an application must be designed to allow alternative execution paths or ways of cleaning up its state so another attempt can be made later. Even so, the power of lock testing and timeouts lies in providing an application with more than one way of getting a task done rather than relying on the anti-deadlock smarts of a single-path algorithm.
Performance
It's most often true that acquiring a
contended lock causes a bigger performance hit than acquiring an
uncontended lock. One way to avoid this performance hit is to actively
manage an application's concurrency by making sure as little work as
possible is done under locking. The goal should be to obtain a lock, do
whatever is necessary, and then release the lock quickly. If some
time-consuming task has to be done, it should be moved out of the
locked area wherever possible.
Another way to improve an application's concurrency is to use a ReentrantReadWriteLock. Rather than creating a blanket mutually exclusive lock, as in the case of the ReentrantLock, the ReentrantReadWriteLock defines reading and writing locks. A write lock is still mutually exclusive, but if none of these is active, then a read lock can be held by more than one reader thread at once.
The ReentrantReadWriteLock works best in situations where there will be more reader threads than writer threads as when working with a Lightweight Directory Access Protocol (LDAP)-like data structure. Whether the ReentrantReadWriteLock delivers any noticeable performance benefits really depends on this imbalance between reader and writer threads being maintained. Still, it's another tool that developers can call on.
Which Method to Use, Old or New?
Even though the
locking utilities in J2SE 5.0 do the same as the existing concurrency
primitives and much more, they aren't automatic first-choice
replacements. So, at some point the question will arise of which to
use. This will naturally depend on the nature of the application being
built, but there are some rules of thumb to help decide between the two.
First, avoid using both if at all possible. Unmistakeably, concurrent applications are more complex to design, code, and debug than sequential applications. Keeping an application as simple as necessary could mean avoiding all flavours of handcrafted concurrency. Still, there may be functional alternatives in the existing thread-safe collections, synchronization wrappers, or some of the new concurrent collections. This pushes the responsibility for thread management onto tried -and tested fundamental Java classes.
But if an application's design calls for a choice to be made, using the synchronized keyword produces code that is simpler and more concise. For example, the single synchronized keyword acquires an object's lock and ensures that it's released when the method or block exits, whether by normal means or by exception. Meanwhile, the new utilities rely on the idiom of explicitly acquiring a lock and then releasing it inside a finally block; it's the developer's responsibility to get this right.
Using synchronized can also have advantages when debugging applications. Because lock acquisition and release is handled by the JVM, the JVM is able to provide locking information when generating thread dumps. Meanwhile, the Lock implementations are vanilla Java classes and the same level of debugging detail isn't available.
Still, the features of the J2SE 5.0 locking utilities are alluring. Multiple condition variables, timed lock waits, interruptible lock waits, and broader lock scope offer developers great power. Doug Lea led the JSR 166 (Concurrency Utilities) specification team and the bulk of the new utilities come from his util.concurrent package, which has been around since the late 1990s. This means the utilities have been peer-reviewed and stress-tested over time to deliver the best possible performance and scalability.
So, when choosing between the old and new exclusion techniques, the best advice would be: start out with the synchronized keyword until it proves to be inadequate, then move onto the new utilities when the need for the extra features or performance is justified.
References
- Lea, D. Concurrent Programming in Java. Second Edition. Addison-Wesley. (1999).
- Oaks, S. & Wong, H. Java Threads. Third Edition. O'Reilly Media. (2004).
- JSR 166: Concurrency Utilities: www.jcp.org/en/jsr/detail?id=166
- Sun's Threads Tutorial: http://java.sun.com/docs/books/tutorial/essential/threads/index.html
Published April 21, 2006 Reads 25,291
Copyright © 2006 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Craig Caulfield
Craig Caulfield is a senior software engineer for a defense and commercial software house in Perth, Western Australia. He has a Bachelors degree in Computer Science, a Masters degree in Software Engineering, and holds certifications in Java, XML, DB2, UML, MySQL, and WebSphere.
![]() |
SYS-CON Brazil News Desk 04/21/06 01:50:09 PM EDT | |||
In concurrent programming, exclusion refers to any technique that dynamically locks certain blocks of code so multiple threads can't corrupt their shared resources in ways that can cause integrity problems. In Java, exclusion has meant using the synchronized keyword against a method or block of code to control access to an object's lock. |
||||
![]() |
SYS-CON India News Desk 04/21/06 01:03:31 PM EDT | |||
In concurrent programming, exclusion refers to any technique that dynamically locks certain blocks of code so multiple threads can't corrupt their shared resources in ways that can cause integrity problems. In Java, exclusion has meant using the synchronized keyword against a method or block of code to control access to an object's lock. |
||||
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- Confessions of a Ulitzer Addict
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- It's the Java vs. C++ Shootout Revisited!
- Cloud Computing Can Revitalize Your Career as Software Developer
- IBM Could "Reinvent" Java: Mills
- Oracle & Cloud Computing: Exclusive Q&A with SVP Richard Sarwal
- A Brief History of Cloud Computing
- Kindle 2 vs Nook
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Why IBM’s Server Chief Got Busted
- Is Cloud Computing Like Teenage Sex?
- Industry Experts Discuss the State of Cloud Computing
- Performance Tuning Essentials for Java
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- My Thoughts on Ulitzer
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- i-Technology Predictions for 2007: Where's It All Headed?





































