Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 4
07:55:38.545: The Speaker requests the lock.
07:55:38.572: The Speaker acquires the lock.
07:55:38.573: The Speaker takes a nap--a model for real work it might otherwise do.
07:55:38.541: The Listener requests the lock.
07:55:39.876: The Speaker releases the lock.
07:55:39.877: The Listener acquires the lock.
07:55:39.878: The Listener just received 1 from the Speaker.
07:55:39.879: The Listener releases the lock.
07:55:39.880: The Listener requests the lock.
07:55:39.881: The Listener acquires the lock.
07:55:39.881: The Speaker has sent nothing.
07:55:39.883: The Speaker requests the lock.
07:55:39.882: The Listener will check again later.
07:55:40.684: The Listener releases the lock.
07:55:40.685: The Speaker acquires the lock.
07:55:40.685: The Speaker takes a nap--a model for real work it might otherwise do.
07:55:40.687: The Listener requests the lock.
07:55:41.330: The Speaker releases the lock.
07:55:41.331: The Speaker waits for the Listener to catch up.
07:55:41.332: The Listener acquires the lock.
07:55:41.333: The Listener just received 2 from the Speaker.
07:55:41.335: The Listener releases the lock.
07:55:41.335: The Listener requests the lock.
07:55:41.336: The Listener acquires the lock.
07:55:41.337: The Speaker has sent nothing.
07:55:41.339: The Listener will check again later.
07:55:41.362: The Speaker requests the lock.
07:55:42.140: The Listener releases the lock.
07:55:42.142: The Speaker acquires the lock.
07:55:42.142: The Speaker takes a nap--a model for real work it might otherwise do.
07:55:42.143: The Listener requests the lock.
07:55:43.606: The Speaker releases the lock.
07:55:43.608: The Speaker waits for the Listener to catch up.
07:55:43.608: The Listener acquires the lock.
07:55:43.611: The Listener just received 3 from the Speaker.
07:55:43.612: The Listener releases the lock.
07:55:43.613: The Listener requests the lock.
07:55:43.614: The Listener acquires the lock.
07:55:43.615: The Speaker has sent nothing.
07:55:43.615: The Listener will check again later.
07:55:43.638: The Speaker requests the lock.
07:55:44.416: The Listener releases the lock.
07:55:44.417: The Speaker acquires the lock.
07:55:44.417: The Speaker takes a nap--a model for real work it might otherwise do.
07:55:44.417: The Listener requests the lock.
07:55:45.433: The Speaker releases the lock.
07:55:45.434: The Speaker waits for the Listener to catch up.
07:55:45.435: The Listener acquires the lock.
07:55:45.436: The Listener just received 4 from the Speaker.
07:55:45.437: The Listener releases the lock.
...
Note that ReentrantLock() permits the Speaker to deliver its message reliably, without the risk of overwriting or the inefficiencies of polling. As bulky as this source
code is, it's significantly more compact than most correctly implemented locking programs that use more primitive threading
constructs.
Whereas we've been talking so far about programming constructs to use, a deadlock is something to avoid. In fact, some say deadlock is an anti-pattern.
See Obi Ezechukwu's three-part series introducing deadlock anti-patterns:
A deadlock occurs when more than one actor is waiting on another actor in a cycle that has no exit. For instance, suppose one thread
were waiting on account.balance to be freed so that it could finish its computation of account.available_for_investment. Then, suppose that the thread holding account.balance were waiting for account.available_for_investment in order to compute account.balance. In a situation where both threads are stuck waiting indefinitely, we have what's known as a deadlock.
Deadlock analysis is one of the fundamental, unavoidable challenges of multithreaded programming. Specialized "best practices" developed in this area include calculation of a hierarchy of critical sections. The next section introduces some lightweight techniques to avoid deadlock.
You might have noticed that the RockScissorsPaper example at the beginning of this article relies on a thread pool. A thread pool (sometimes called a worker thread crew or a replicated thread worker collection ) is a team of threads managed in coordination. Thread pools are often implemented to receive assignments of pending tasks. Management at the pool level has at least the potential to improve performance by eliminating the cost of creating and cleaning up one thread for each task.
The RockScissorsPaper program, for instance, could have used distinct new Thread() instances, and start()ed each one separately. Instead, I wrote the program to submit() them to a thread pool. The thread pool was then responsible for ensuring that each thread was properly scheduled for execution.
See "Hyper threaded Java: Using the Java concurrency API for time-consuming tasks" for an in-depth discussion of thread pools and the ThreadPoolExecutor utility class. Then follow up with "Hanging thread detection and handling," which introduces a custom thread pool with built-in hanging detection.
Thread pooling is a common and very useful practice among languages that manage threads, and Java does well to make it available in the standard library. Thread pools are commonest when the number of tasks assigned to the pool is larger than the number of threads available to run those tasks. Wisely managing threads can greatly accelerate task completion in such a scenario.
Brian Goetz, et al., discussed thread pools and the Executor interface in Java Concurrency in Practice (Addison-Wesley, 2006). See the chapter excerpt on executing tasks in threads to learn more about the theory behind the RockScissorsPaper program in Listing 1.
The biggest problem with multithreaded programming is that it's so commonplace. Having programmed with Threads once or twice, some new developers will conclude that they understand Java concurrency.
In fact, multithreading is a deep and broad subject, far more so than any one program -- or even a handful of them -- could
capture. In this article I've presented only a fraction of the concurrency constructs that are standard to the Java platform,
both pre- and post-java.util.concurrent. Moreover, my examples don't exhaust the capabilities of thread pools, locks, and so on: nearly all the specific method invocations
above assume several default values. As you become more expert with threading, you'll encounter real-world requirements that
make it to your advantage to use the concurrent application programming interface (API) in more sophisticated ways. You'll refine your method calls with non-default arguments,
and you'll learn more advanced method calls, to match the specific needs of your programs.
Getting good at threaded programming is an investment worthy of at least 10,000 hours. Let the material here serve as a starting point for your experimentation.
Cameron Laird began writing Java code before it was called Java and has contributed occasionally to JavaWorld in the years since then. Keep up with his coding and writing through Twitter as @Phaseit.
Read more about Core Java in JavaWorld's Core Java section.
Learn more about topics related to multithreaded programming and concurrency on the Java platform.
java.util.concurrent
Callable and Runnable
Wait/notify
Thread safety