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:
- deadlock. Two threads hold locks on different resources, each waiting indefinitely for the other to release its lock.
- 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:
- public class MyServlet extends HttpServlet {
- private Person currentUser = new Person();
-
- public void doGet(HttpServletRequest req, HttpServletResponse res)
- throws ServletException, IOException {
- currentUser.setFirstName(req.getParameter("firstName"));
- currentUser.setLastName(req.getParameter("lastName"));
-
- // remainder of code omitted...this Servlet might perform some
- // operations on the currentUser object, such as storing the
- // data in a database...
- }
- }
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:
- Make the shared resource thread safe.
- 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:
- public class MyServlet extends HttpServlet {
-
- public void doGet(HttpServletRequest req, HttpServletResponse res)
- throws ServletException, IOException {
- Person currentUser = new Person();
- currentUser.setFirstName(req.getParameter("firstName"));
- currentUser.setFirstName(req.getParameter("lastName"));
-
- // remainder of code omitted...
- }
- }
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.
- // example immutable object
- public class Dog {
- private String name;
- private Date birthDate;
-
- public Dog(String name, Date birthDate) {
- this.name = name;
- this.birthDate = birthDate;
- }
-
- public String getName() {
- return name;
- }
-
- public Date getBirthDate() {
- // see note below!
- return (Date) birthDate.clone();
- }
- }
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:
- // This class provides thread-safe methods to add/remove/update dogs.
- // An actual implementation may use a relational database as its
- // underlying storage mechanism.
- public class Kennel {
- public synchronized void create(Dog dog) { ... }
- public synchronized void update(Dog orig, Dog modified) { ... }
- public synchronized void delete(Dog victim) { ... }
- public synchronized Dog find(String name) { ... }
- ...other methods
- }
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:
- Use immutable objects whenever possible.
- Use local variables.
- When data must be shared, provide controlled access to that data through some sort of "manager," like the Kennel example above.
- Use the synchronized keyword when needed, but be aware that excessive synchronization can harm performance and may increase the likelihood of deadlocks.
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.