Read this detailed review of O'Reilly's 'Java Threads' to find out if you should invest in a copy
Java Threads isnโt your run of the mill Teach Yourself Java in X Days type of book. As Scott Oaks and Henry Wong, the authors of Java Threads, explain, โThis book belongs to the second wave of Java books: because it covers only a single topic, it has the luxury of explaining in deeper detail how Javaโs threads can be used.โ And this book does indeed delve into deep detail. Considering that Javaโs multithreading capabilities are neatly encapsulated in barely more than a single class โ java.lang.Thread and class Objectโs wait() and notify() methods โ and are supported by the language itself via a single keyword, synchronized, it follows that Java Threadsโs 250-odd pages explore many a dark multithreaded corner.
In this review Iโll provide a blow-by-blow account of each of the eight chapters and four appendices, followed by a few miscellaneous items of note for anyone considering purchasing this book.
http://www.ora.com/catalog/jthreads/ ISBN 1-56592-216-6, 268 pages 9.95 |
Chapter and verse
Chapter 1 is the obligatory introduction to the subject matter. It explains the difference between multitasking and multithreading and why, contrary to most other programming languages, threading is so important to Java programming. The answer to the latter question simply being that all Java I/O is synchronous and that you need extra threads to avoid your applications screeching to an immediate halt whenever they access the Internet, read files, or wait for some form of timeout.
Chapter 2, โThe Java Threading API,โ introduces the Thread class and the associated, pivotal Runnable interface. The authors examine the start(), stop(), and sleep() using a simple, multithreaded applet example, and then switch the focus to the join() method, which is used to wait for the completion of some other thread.
At this point, I think it is worth mentioning two things about the examples provided with this book: first, the book does not include a disk (floppy or CD) of source code. My initial response to this discovery was quite positive, figuring Iโd just head over to the OโReilly Web site and pick up whatever I needed; unfortunately, there is no link to such a site. My second gripe is that many of the programs are incomplete (that is, they wonโt compile because they are not meant to be compilable examples) or hypothetical in nature (where 90 percent of the program is left to the readerโs imagination). In short, the examples do not provide insight into the behavior of running threads. Worse, because you cannot check any of the examples, you are sometimes left reading large amounts of text and code โon faith.โ
Chapter 2 also introduces the threading pitfall par excellence: race conditions. Readers follow along as the authors develop an AsyncReadSocket class that loses data due to a (purposely) naive lack of protection against multithreaded access of this network programming class. (Yes, the authors are Unix programmers, so they think nothing of relying on your development platform being permanently connected to a TCP/IP network, or using some advanced socket programming to make a point about multithreading. To mortals with dial-up access or novices in the art of TCP/IP programming: tough luck.)
Chapter 3, โSynchronization Techniques,โ uses a much more universally understandable example to explain the core concept of thread synchronization. The authors use the classic example of simultaneous access to a bank account to illustrate how you can eliminate race conditions by protecting some critical code sections using Javaโs synchronized keyword.
The synchronized mechanism lies at the heart of most of Javaโs inter-thread communication solutions. The synchronized keyword turns a code block or an entire method into a critical section by requiring the executing thread to obtain a lock on the current object before entering the synchronized code. On exiting the code, the lock is released.
Right about this time the discussion takes a confusing turn. Right after elegantly solving the banking exampleโs race condition (to complete satisfaction) by simply making the methods that manipulate the shared bank account data synchronized, the authors suddenly attempt to write a BusyFlag class whose objects can be used as synchronization primitives โ without using the newly found hero (the synchronized keyword). Not surprisingly, they conclude that their BusyFlag class contains a race condition (it is impossible to write any synchronization mechanism from scratch without relying on Javaโs synchronized keyword in one way or other).
While introducing the concept of deadlock and progressing into chapter 4, โWait and Notify,โ this BusyFlag class goes through several confusing incarnations, which the authors themselves admit continues to harbor a race condition. I found that the contrived implementation and โenhancementsโ of this class significantly harmed an otherwise clearly written section whose sole intent was to demonstrate that Javaโs synchronized keyword can only provide fine-grained protection up to method scope. When an entire transaction needs to be atomic (to prevent race conditions occurring in entire subsystems, for example), you need to craft your own higher-level semaphore or mutex class. And that is what the BusyFlag class was all about. Or rather, the class is reused or referred to in several more examples in the remainder of the book.
With chapter 5, โUseful Examples of Java Thread Programming,โ all the jargon, concepts, and API explained to this point is put into practice. The chapter starts by developing some thread-safe data structures like a MessageQueue, SharedMemory, and a CircularList. Then we dive back into network programming with a generic TCPServer class. Another interesting example is the AsyncInputStream class, which allows you to read from any InputStream in an asynchronous fashion. In a nutshell, the class spawns a thread to do the real stream reading (and can therefore block right there and then), while clients of the class can safely read from the input buffer that this thread constantly attempts to fill up, without ever blocking. Aside from being a nice multithreading example, the issues of caching EOF and thrown exceptions was also enlightening.
Chapter 6, โJava Thread Scheduling,โ ponders why the Java language does not specify how threads are scheduled (a preemptive scheme, or time-sliced, or a more primitive run-to-completion scheme), and discusses how this impacts your multithreaded applications. A platform-dependent (read: serious) problem occurs if a multithreaded Java program contains CPU-bound threads (like a time-consuming rendering thread). Because Java does not guarantee time-sliced scheduling of threads (when of equal priority), any CPU-bound threads could starve all other threads from access to the CPU. This only occurs on Java host platforms like Unix where the host OS usually does not use preemptive scheduling. In other situations it may be more fair to let threads run to their logical completion to optimize average server performance in a client-server system. Either way, Javaโs lack of an explicit scheduling approach means that you need to overlay your own application-level scheduler on top of Javaโs. This is exactly what readers get to do in chapter 6 โ develop class CPUScheduler: a time-slicing, round-robin scheduler, the first incarnation of which is a thoroughly impressive sixteen lines of Java code (now try beating that in C++!).
For the remaining 20 pages, readers make various improvements to the initial scheduler, ultimately resulting in relying on thread priorities to manage a pool of threads submitted to the CPUScheduler.
Chapter 7, โAdvanced Synchronization Topics,โ further studies deadlocking scenarios like lock starvation. This chapter actually contains very interesting high-level, language-independent analyses of which factors are needed for certain types of deadlock to occur. The chapter also discusses reader-writer locks as a form of lock that has been optimized to grant multiple access to reading threads, but only allow access to a single writing thread.
The final (and quite brief) chapter, โThread Groups,โ focuses on the java.lang.ThreadGroup class and its uses. Because class ThreadGroup lets applications, and more specifically applets, have low-level information on potentially security-compromising systems (like the garbage-collecting thread), the authors include a discussion of thread security issues and the central role of the SecurityManager class.
The remaining 40 pages of Java Threads is devoted to its four appendices. Appendix A combines several topics too small to justify a chapter on their own. Youโll find information on the interrupt() method, the ThreadDeath class, the thread execution stack, and how you can write your own default exception handler for exceptions that are not caught by your run() method. Appendix B enumerates some of the more important exceptions and errors that can occur when programming with more than one thread.
Appendix C is a very interesting look under the hood of the Java API and analyses how the API itself relies on multithreading to manage timing, garbage collection, object finalization, AWT event dispatching, AWT window refresh, playing audio, and loading and displaying images. The appendix climaxes with an eye-opening table listing the minimum threads that run when a Java applet is using audio and images (no less than 16 threads!). Due to its highly concrete and mystery-busting qualities, I found that this appendix would have been better promoted and expanded to fill an entire chapter. The last appendix is barely more than a quick introduction to the thread features of JDB, the most user-unfriendly JDK debugger.
After the fact
Like all Java books, this book suffers from not being able to keep up with Java API developments. Although this first edition is dated 1997, it explicitly limits itself to dealing with JDK 1.0.2. This is unfortunate because method
Thread.interrupt()
(and the associated
InterruptedException
thrown by methods such as
sleep()
) is now fully implemented in JDK 1.1. Had the
interrupt()
method been available to the authors when they set about writing this book, it surely would have impacted their manuscript in many places โ
interrupt()
, in combination with
sleep()
, can replace the much more complex
wait()
/
notify()
duo that has to be used in conjunction with Javaโs
synchronized
keyword.
sleep()
and
interrupt()
are free of this requirement and are therefore more flexible (not to mention a whole lot easier to understand).
The authors are obviously quite fond of footnotes because the entire book is littered with them. What bothered me about the footnotes was not their frequency (although Iโll cop to a bit of annoyance on this front), but that the asterisks, daggers, and double-daggers scattered throughout the text referred to more than peripheral facts or anecdotes: the vast majority of footnotes were actually core text material and should have been skillfully incorporated in the main text. Because the footnotes contain important information, you are constantly switching between the main text and those โfootnotesโ that shouldnโt be.
The book also exhausted its quota of permissible errors. I noted several editing mistakes โ some minor, some not-so-minor. Far worse than any editing mistakes are factual errors: Page 54 claims that Java does not guarantee the assignment operator be atomic for object (reference) types. This is obviously incorrect as it would become almost unbearable to have to enclose any single assignment operations in a synchronized block (more importantly, the official Java language spec confirms the error!). Given this โoversight,โ it is no surprise that the book also omitted the fact that assignment of longs or doubles (the only 64-bit types in Java) really is not guaranteed to be atomic and that these (most exceptionally) have to be protected using a suitable locking mechanism.
The code examples also contained some minor annoyances like the frequent use of old-fashioned, C-style one- or two-letter variable identifiers (something that I find totally irresponsible in a modern text book). One code segment, for example, used โtโ as a formal parameter name in two consecutive methods for totally different parameters (an int time and a thread). I also noted instances of variable identifiers that ignore the Java convention of starting a variable with a lowercase character.
Arguably a lot more important is the way the majority of examples seem to ignore exception handling: the authors mostly used empty catch clauses, but some examples used the highly questionable technique of catching Exception itself instead of the specific exception type(s) thrown by any method(s) inside the try block. (Catching Exception in this way stops bug-detecting exceptions like NullPointerException from being caught where they occur โ a serious problem when the technique is used in large programs).
Despite the few points of criticism I have for Java Threads, I am glad that such a book was written. The topic of Java threads is often treated as an aside in other books while, like the authors of Java Threads correctly point out, multithreading in Java is really fundamental to every non-trivial Java program. I would like to see a second edition that reflects the latest JDK release, but if you canโt wait until that currently unspecified time, you will get value for money by buying a copy of Java Threads now.


