Thread « Development « Java Articles

Home
Java Articles
1.Build Deploy
2.Class
3.Core Library
4.Data Types
5.Database JDBC
6.Design
7.Development
8.File Input Output
9.Graphics Desktop
10.J2EE Enterprise
11.J2ME Wireless
12.JVM
13.Language
14.Library Product
15.Network
16.Security
17.SOA Web Services
18.Test
19.Web Development
20.XML
Java Articles » Development » Thread 
The first thread-related problem with Observer shows up when you implement a listener with an inner class. This example really drives home the fact that race conditions can appear even in situations where you have written no explicit multithreaded code at all because several of the Java packages (most notably AWT/Swing) create threads of their own.

As it stands, this code is a multithreading disaster. Nothing is synchronized and we have guaranteed race conditions. (A race condition occurs when two threads try to access the same object at the same time, and chance determines which one wins the "race." Programs shouldn't work by chance.) Synchronizing all the methods would fix the problem, but then you couldn't call a method in partition 1 (emphasized in the code above) simply because some thread was using a method from partition 2 above. Since these two partitions don't interact with each other, this solution imposes needless access restrictions on the methods of the class. If you're accessing any method in partition 3, though, you do want to lock out everything in the other two partitions. We really need two locks in this situation. One to lock partition-1 variables and another for partition-2 variables. The methods in partition 3 can then grab both locks.

So, what's so upsetting? It seems Sun has abandoned the "run anywhere" part of its oft-quoted write-once, run-anywhere mantra. I'll explain. The ability to "run anywhere" is predicated on a class file being able to run on any machine that has a Java virtual machine (JVM) on it, and on that VM being able to run any class file it's given. If different implementations of the JVM recognize different class file formats, platform independence is a pipe dream. Microsoft understands this issue very well. That's why it tried to push a nonconforming VM onto Windows. Programs that leverage Microsoft's nonstandard extensions simply are not platform-independent.

This article, the second in a four-part series that explores threads, teaches you how to use synchronization to serialize thread access to critical code sections. I begin with an example that illustrates why some multithreaded programs must use synchronization. I next explore Java's synchronization mechanism in terms of monitors and locks, and the synchronized keyword. Because incorrectly using the synchronization mechanism negates its benefits, I conclude by investigating two problems that result from such misuse.

Because multithreading is built into Java, it is possible that any class you design eventually may be used concurrently by multiple threads. You needn't (and shouldn't) make every class you design thread-safe, because thread safety doesn't come for free. But you should at least think about thread safety every time you design a Java class. You'll find a discussion of the costs of thread safety and guidelines concerning when to make classes thread-safe later in this article.

The first of these, opening a named channel, requires that the class be able to share information (the channel registry) between threads. In Java, threads typically run in a shared address space. Subsequently, static fields in a Java class are shared across all instances of that class. You will recall that static instance variables (sometimes called class variables) do not require that an object instance be created to access them. Instead, they may be accessed using the Classname.variable_name syntax. For the purposes of our example, however, the more salient property is that they are visible to all threads.

My previous three articles explored an assortment of thread concepts: the Thread class, the Runnable interface, exceptions and the run() method, synchronization, thread scheduling, the wait/notify mechanism, and thread interruption. This month's Java 101 concludes the thread series by focusing on thread groups, volatility, thread-local variables, timers, and the ThreadDeath class. Want more on threads? The sidebar "Finalization and Threads" describes how various thread concepts combine to finalize objects.

By examining the execution context, the AccessController class decides whether or not to allow a method to execute a piece of security-critical code. More specifically, it makes the determination by examining the protection domains associated with the methods in the execution context. The AccessController class permits the operation if and only if every protection domain in the current execution context holds the appropriate permission. This "principle of least privilege" is a key characteristic of the Java security model.

Lines 14 through 17 execute when it is the current thread's turn to go. When executed, the thread updates the state variable with the next thread's turn. This is done before the notify, as the notify may cause another thread to start running immediately before it knows it is its turn to run. Then notifyAll() is called to notify all threads that are waiting on this object that they can run. If you are using only two threads, simply call notify() since that call will wake up exactly one thread from the set waiting to run. With two threads, only one thread can be waiting, so the correct thread will wake up. If you extend this to three or more threads, however, the notify call may not wake up the correct thread and the system will stop until that thread's wait times out.

A synchronous dispatcher, or round-robin scheduler, solves the synchronization problem by simulating multithreading within a single Java thread. Let's start out by considering two tasks that need to be executed in parallel:

Users hate unresponsive software. When users click a mouse, they expect a program to instantly respond to their requests, even when the program is in the midst of a time-consuming activity, such as repaginating a long document or waiting for a network operation to complete. Programs that respond slowly to their users exhibit poor performance. To improve a program's performance, developers typically use threads.

Java's implementation of threading, in fact, complicates matters by using a misleading metaphor for threads. You have to create a thread by deriving from Thread, which leads many a novice Java programmer to the erroneous belief that all the methods of the Thread derivative will run on that thread. In fact, a method of a Thread derivative is just like any other method: it runs on a thread only if called directly or indirectly from that thread's run() method. Objects do not run on threads; methods do.

Unfortunately, Java's promise of platform independence falls flat on its face in the threads arena. Though it's possible to write a platform-independent multithreaded Java program, you have to do it with your eyes open. This isn't really Java's fault; it's almost impossible to write a truly platform-independent threading system. (Doug Schmidt's ACE [Adaptive Communication Environment] framework is a good, though complex, attempt. See Resources for a link to his program.) So, before I can talk about hard-core Java-programming issues in subsequent installments, I have to discuss the difficulties introduced by the platforms on which the Java virtual machine (JVM) might run.

Just to make sure we're all starting from the same place, a little review of terms is in order. The central concept for synchronization in the Java model is the monitor, developed some 20 years ago by C. A. R. Hoare. A monitor is a body of code guarded by a mutual-exclusion semaphore (or, to use a term coined at Digital Equipment Corp., a mutex). The central notion of a mutex concerns ownership. Only one thread can own the mutex at a time. If a second thread tries to "acquire" ownership, it will block (be suspended) until the owning thread "releases" the mutex. If several threads are all waiting to acquire the same mutex, they will all be released simultaneously when the owning thread releases the mutex. The released threads will then have to sort out amongst themselves who gains ownership. (Typically, priority order, FIFO order, or some combination thereof is used to determine which thread gains control.) You guard a block of code by acquiring a mutex at the top of the block and releasing it at the bottom. The code comprising the monitor does not have to be contiguous: several discontiguous code blocks can all acquire and release the same mutex, in which case all of this code is considered to be in the same monitor because it uses a common mutex.

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.

Do you ever get the feeling there's something not quite right about Swing threading? John Zukowski puts his finger on it in this article. While tracing Swing's single-threaded event model from Java 1.0 to its origins in the JavaBeans component model and AWT, he reveals an initialization bug in a commonly used start-up pattern. Fortunately, once you know about the bug, you can fix it. All you have to do is update your interpretation of Swing's single-thread rule.

So far in this series I've been concentrating on the monitor -- a means of locking an entire object while a body of code is being executed. The other essential sort of lock you should be aware of is the critical section. Critical sections are essential in implementing one-time initialization code when that code can be accessed from multiple threads.

(A semaphore is any of several mechanisms used to synchronize and communicate between threads. Think of the "semaphore" as in the flags that two boy scouts use to talk to each other from a distance -- different flag positions represent different letters of the alphabet. Napoleon's army used the vanes of windmills on mountain tops to send semaphore messages great distances very quickly. The mutex discussed last month, since it's a communications mechanism, is also a "semaphore.")

The setProperties()'s intent is undoubtedly to have the new system properties available to all Threads in the JVM, not just the calling one. However, nothing ensures that in the code. No memory barriers either in the form of volatile or synchronized ensure that a caller of getProperty() will look inside the java.util.Properties instance installed by the last call to setProperties(). You can find more cases of the same problem in the JDK: java.net.Authenticator.setDefault()/requestPasswordAthentication(), java.sql.DriverManager.get/setLoginTimeout(), java.net.HttpURLConnection.get/setFollowRedirects(), and so on. java.util.Locale.get/setDefault() had the same problem in JDK 1.3 and changed to have a milder double-check locking problem in JDK 1.4 (see Note in Resources).

Is it better to use if or a loop (such as while)? For ProdCons2, either approach is fine—because I've carefully coded my programs to ensure that a thread sets the appropriate condition variable (on which another thread waits) prior to waking that thread. For complex programs, where you are uncertain that a waiting thread's condition variable will be set to an appropriate value prior to waking that thread, you should use a loop. That way, the just-woken thread can retest its condition variable and wait if that variable contains the wrong value.

Most concurrent applications are organized around the execution of tasks: abstract, discrete units of work. Dividing the work of an application into tasks simplifies program organization, facilitates error recovery by providing natural transaction boundaries, and promotes concurrency by providing a natural structure for parallelizing work.

Multithreaded applications deliver their potent power by running many threads concurrently within a single program. From a logical point of view, multithreading means multiple lines of a single program can be executed at the same time, however, it is not the same as starting a program twice and saying that there are multiple lines of a program being executed at the same time. In this case, the operating system is treating the programs as two separate and distinct processes. Under Unix, forking a process creates a child process with a different address space for both code and data. However, fork() creates a lot of overhead for the operating system, making it a very CPU-intensive operation. By starting a thread instead, an efficient path of execution is created while still sharing the original data area from the parent. The idea of sharing the data area is very beneficial, but brings up some areas of concern that we'll discuss later.

In this last column on threads and interapplet communication I'll look at a couple issues associated with this approach, and in particular I'll discuss how a layer can be created on top of the existing DataChannel design to allow multiple DataChannels to feed into a single DataChannel. The source code is also online and available for your use, in either an elaborate form, or as a tar or zip archive.

This month, I continue my four-part thread series by focusing on thread scheduling, the wait/notify mechanism, and thread interruption. You'll investigate how either a JVM or an operating-system thread scheduler chooses the next thread for execution. As you'll discover, priority is important to a thread scheduler's choice. You'll examine how a thread waits until it receives notification from another thread before it continues execution and learn how to use the wait/notify mechanism for coordinating the execution of two threads in a producer-consumer relationship. Finally, you'll learn how to prematurely awaken either a sleeping or a waiting thread for thread termination or other tasks. I'll also teach you how a thread that is neither sleeping nor waiting detects an interruption request from another thread.

The first question to ask in the problem-solution method is, of course: What is the problem? The problem statement is easy to generate in existing systems since the problem generally causes the trouble. In this case, the problem is: The threads die, and the application stops running. No exception is thrown since the cause of thread death is external to the application.

These new systems give you, the high-performance Java developer, a tremendous opportunity for speeding up your programs. However, they won't inherit the advantages of SMT automatically; you must adapt your algorithms so the time-consuming sections are performed by multiple threads designed to run simultaneously. The addition of high-level concurrency utilities to Java 5 greatly eases this process. Using two important classes from the package java.util.concurrent, I'll demonstrate how to speed up time-consuming tasks by having them use the optimal number of threads for the systems on which they are executed.

Threads are powerful constructs available for software programming, which we have been leveraging even from the earliest versions of Java. But many times programs may not run efficiently due to non-optimized usage of system resources, including threads. Thread pools are yet another powerful tool, which even a less-experienced programmer can use to write optimized parallel programming code. This article showed you how you can apply parallelism in middle-tier Java programming and at the same time apply split-and-query mechanisms at the database level, so that we extend optimization patterns across multiple tiers in our application.

Editor's note: Sometimes the most interesting discussions begin when someone says, "This may be a stupid question, but ...." If the person asking the question has taken the time to think about the problem before asking, the question is often not stupid at all. The uncertainty points out an ambiguity in the specs, holes in the docs, or a search for how more experienced programmers might address a particular problem. From time to time, we will print one of the "(Not So) Stupid Questions" we receive and invite our readers to answer the question in the feedback section.

A Lesson in Networking: Server Thread Code for Java by Robert Uonimi ? Release JDK 1.1 Instructive code for learning how to do networking and threads. (September 1997)

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.

RGBColor # 2: Thread safety through synchronization Here's a revised version of the RGBColor() class. This version, which has its critical sections marked as synchronized, is thread-safe:

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.

How the app knows to terminate: A Java application terminates when all non-daemon threads expire. In this application, there are three non-daemon threads. The main thread sets up and starts the other two threads, then returns. One thread down. The FibonacciGenerator thread generates the numbers, stores them into the LongBuffer, then writes an END_OF_DATA into the LongBuffer, and returns. By returning from run(), the FibonacciGenerator thread expires. Two threads down. The LongBufferToOutputThread reads from the LongBuffer and writes to the standard output until it finds an END_OF_DATA in the LongBuffer. It then returns. By returning from run(), the LongBufferToOutputThread thread expires. Because this is the third and only remaining non-daemon thread, the entire application terminates.

In consider thread-safety when designing any concrete class, talk about applying thread safety consciously, including the option of immutability and using thread-safe wrappers. Mention that in the semantic description, things should be assumed to be thread safe, unless explicitly says that it isn't thread safe.

Creating a Java Thread So, how does one actually create a multithreaded program? The following code composes a simple program that will demonstrate the fundamentals. This simple, non-threaded, class exposes a method that merely loops from 0 to 100, printing "Hello world" on each iteration.

Both the thread-per-task and the single-background-thread approaches can work perfectly well in certain situations. The thread-per-task approach works quite well with a small number of long-running tasks. The single-background-thread approach works quite well so long as scheduling predictability is not important, as is the case with low-priority background tasks. However, most server applications are oriented around processing large numbers of short-lived tasks or subtasks, and it is desirable to have a mechanism for efficiently processing these tasks with low overhead, as well as some measure of resource management and timing predictability. Thread pools offer these advantages.

In July's installment of Java theory and practice, we looked at thread pools, and examined how an improperly written thread pool could "leak" threads, until eventually all the threads were gone. Most thread pool implementations guard against this by catching thrown exceptions or restarting threads that die, but the problem of thread leakage is not limited to thread pools -- server applications that use threads to service work queues can have this problem too. When a server application loses a worker thread, the application may appear to work fine for quite a while, making the true cause of the problem difficult to identify.

In a case such as this, the second thread is entirely dependent upon the first to receive any data with which to compute. It is, therefore, inevitable that if the first thread crashes (and, in this sample, it is guaranteed to do so), the second thread will wait for further input that will never come. Now you know why I call this pattern of bug the orphaned thread pattern.

Java programming language offers powerful language support for writing multi-threaded applications. However, writing useful and bug-free multi-threaded programs is still difficult. This article attempts to outline some of the methods programmers can use to create thread-safe classes that are also efficient.

There are times when you need to specify the order of execution of waiting threads. The Java platform does not support this need inherently. You can create a programmatic mechanism to control the order of execution of waiting threads by implementing the specific notification pattern discussed in this article. This technique entails creating a separate lock object for each thread, or set of threads, you want to notify. When you have more than one thread per lock object, you use notifyAll() to awaken them. If each thread has its own lock object, you use notify().

In Joshua Bloch's excellent book, Effective Java Programming Language Guide (see Resources), Item 52 is entitled "Document Thread Safety," in which developers are entreated to document in prose exactly what thread safety guarantees are made by the class. This, like most of the advice in Bloch's book, is excellent advice, often repeated, but less often implemented. (Like Bloch says in his Programming Puzzlers talks, "Don't code like my brother.")

ThreadLocal offers a number of benefits. It is often the easiest way to render a stateful class thread-safe, or to encapsulate non-thread-safe classes so that they can safely be used in multithreaded environments. Using ThreadLocal allows us to bypass the complexity of determining when to synchronize in order to achieve thread-safety, and it improves scalability because it doesn't require any synchronization. In addition to simplicity, using ThreadLocal to store a per-thread-singleton or per-thread context information has a valuable documentation perk -- by using a ThreadLocal, it's clear that the object stored in the ThreadLocal is not shared between threads, simplifying the task of determining whether a class is thread-safe or not.

One obvious technique for reducing the likelihood of contention is to make synchronized blocks as short as possible. The shorter the time a thread holds a given lock, the lower the probability that another thread will request it while the first thread is holding it. So while you should use synchronization to access or update shared variables, it is usually better to do any thread-safe pre-processing or post-processing outside of the synchronized block.

Macho competition and obfuscated programming contests aside, in what situations is it valuable, from a software engineering perspective, to be able to perform an arbitrary sequence of operations on an object in a single expression? In the Java? language, several cases exist where being able to instantiate and initialize a complex object in a single expression can improve the code's readability (field initializers, method parameters). There is even one situation where it is downright inconvenient if instantiation and initialization cannot be completed in a single expression (using constructor arguments to instantiate a new object and then passing the new object to a super() or this() constructor).

You have a couple of options in the log/trace arena. You can, of course, go with the "roll your own" technique; there are also precanned support classes, both proprietary and free, offering a range of functionality. One example of a free implementation is log4j, which is well proven, fast, and versatile. It provides a comprehensive infrastructure and easy-to-understand configuration. Thus, I have chosen it as the base for my examples. In this article, I'll walk you though the use of log4j and apply it to a working example of multithreaded, multiplatform code.

The practices detailed above for thread-safe construction take on even more importance when we consider the effects of synchronization. For example, when thread A starts thread B, the Java Language Specification (JLS) guarantees that all variables that were visible to thread A when it starts thread B are visible to thread B, which is effectively like having an implicit synchronization in Thread.start(). If we start a thread from within a constructor, the object under construction is not completely constructed, and so we lose these visibility guarantees.

To demonstrate the CountDownLatch class, I implemented a simple server that processes a number of LineCounter requests in a threaded manner. The definition I chose for the server's interface is simple: It accepts a queue of requests, after which the server can be started.

Thread groups can make life much simpler for programmers, as well as improving error handling. While threads can be stored collectively in an array or Vector, the additional functionality provided by thread groups make them a more attractive option. By grouping threads collectively, you can also have clearer code and an easy way to track how many threads are active.

A casual investigation of the Thread class reveals an assortment of interesting methods for performing basic thread operations. Some of those methods are deprecated (and should not be used). However, other methods are quite useful and can simplify working with threads. This article explores several of Thread's methods.

This article shows you how to create a thread pool in Java. A complete thread pool is included with this article. This thread pool is extensible enough to be added to your own programs to add thread pool functionality. This article will focus on how the thread pool was created and how it is used.

ww__w___.j_a_va_2___s___.__c__om | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.