Tips for Creating Thread-Safe Code (Avoiding Race Conditions)

Tips for Creating Thread-Safe Code (Avoiding Race Conditions)

By Eric Burke, OCI Senior Software Engineer

April 2000


Synopsis

Java has always included support for multi-threaded programming, in which more than one task executes concurrently.

Two basic problems commonly occur in multi-threaded programs:

  1. deadlock. Two threads hold locks on different resources, each waiting indefinitely for the other to release its lock.
  2. race condition. Two (or more) threads alter the state of a shared resource concurrently, leaving it in an unpredictable state.

This article focuses on a few simple programming techniques you can adopt to minimize the possibility of race conditions.

When are Race Conditions an Issue?

Whenever a program utilizes multiple threads, deadlocks and race conditions are possible. Most programs do not have to concern themselves with these issues, but Java Servlets are a notable exception.

With the rapid adoption of Servlet-enabled web applications, many Java programmers are faced with threading issues for the first time.

Servlets are analogous to CGI scripts – they are Java programs that are used with a web server to create dynamic web applications. Each Servlet must be thread safe, because it may be asked to handle hundreds or thousands of client requests concurrently, each in a different thread.

Identifying Threading Problems

Race conditions are the most frequent threading problem.

A simple example follows:

  1. public class MyServlet extends HttpServlet {
  2. private Person currentUser = new Person();
  3.  
  4. public void doGet(HttpServletRequest req, HttpServletResponse res)
  5. throws ServletException, IOException {
  6. currentUser.setFirstName(req.getParameter("firstName"));
  7. currentUser.setLastName(req.getParameter("lastName"));
  8.  
  9. // remainder of code omitted...this Servlet might perform some
  10. // operations on the currentUser object, such as storing the
  11. // data in a database...
  12. }
  13. }

In this example, the doGet method is updating the currentUser object.

Since this is a Servlet, the doGet method may be invoked by many threads concurrently. A thread that is in the middle of an invocation of doGet may be interrupted, and some other thread may overwrite the first and last name of the currentUser object.

This is an example of a shared resource being updated by multiple threads concurrently.

In this case, the resource is a private field: currentUser. In other cases, the shared resource may be a flat file or a database connection.

Fixing the Problem

There are two general approaches:

  1. Make the shared resource thread safe.
  2. Ensure that no two threads ever update the resource concurrently.

In this case, synchronizing individual methods in the Person class will not help, because you are making several independent method calls to the same currentUser object.

One naive approach is to make the entire doGet method synchronized.

A synchronized method can only be executed by a single thread at a time.

If synchronized, this Servlet will only be able to handle one user at a time. All other users will be served one at a time, as each thread takes its turn executing the doGet method.

A better approach is to make the Person object local. Local variables are thread safe, because every thread gets a unique copy.

The example would be changed to:

  1. public class MyServlet extends HttpServlet {
  2.  
  3. public void doGet(HttpServletRequest req, HttpServletResponse res)
  4. throws ServletException, IOException {
  5. Person currentUser = new Person();
  6. currentUser.setFirstName(req.getParameter("firstName"));
  7. currentUser.setFirstName(req.getParameter("lastName"));
  8.  
  9. // remainder of code omitted...
  10. }
  11. }

This code is now thread safe, and it does not incur the same performance penalty as making the entire method synchronized.

Immutable Objects

An immutable object is one that cannot be modified after it has been constructed. These are guaranteed to be thread safe, because no two threads (or anybody!) can concurrently modify data in them.

Constructors, by the way, are automatically thread safe, as well.

  1. // example immutable object
  2. public class Dog {
  3. private String name;
  4. private Date birthDate;
  5.  
  6. public Dog(String name, Date birthDate) {
  7. this.name = name;
  8. this.birthDate = birthDate;
  9. }
  10.  
  11. public String getName() {
  12. return name;
  13. }
  14.  
  15. public Date getBirthDate() {
  16. // see note below!
  17. return (Date) birthDate.clone();
  18. }
  19. }

Creating immutable objects can be tricky.

In the above example, the birthDate field can be modified, so we don't want to return a reference to it. Instead, you can clone the object and return a copy. This ensures that nobody can modify the Dog instance.

Immutable objects do not prevent you from changing data. You just have to replace the existing object with a new one that has your updated data.

One simple approach is to use a manager object (which is thread safe) to create and replace immutable objects. For example:

  1. // This class provides thread-safe methods to add/remove/update dogs.
  2. // An actual implementation may use a relational database as its
  3. // underlying storage mechanism.
  4. public class Kennel {
  5. public synchronized void create(Dog dog) { ... }
  6. public synchronized void update(Dog orig, Dog modified) { ... }
  7. public synchronized void delete(Dog victim) { ... }
  8. public synchronized Dog find(String name) { ... }
  9. ...other methods
  10. }

Although the Dog itself cannot be modified, you can obtain an existing object and replace it with a new one by using the update(...) method in Kennel.

Summary

Some general strategies for making objects thread safe include:

Future Research

Java 2 (a.k.a. JDK 1.2) adds a new class called ThreadLocal, which makes it easy to create and use variables that are local to a given thread. If these variables are not shared with multiple threads, then it follows that they are thread safe.



Software Engineering Tech Trends (SETT) is a regular publication featuring emerging trends in software engineering.