Using threads with collections, Part 1
Threads add a new degree of complexity to otherwise straightforward design issues
By Chuck Mcmanis, JavaWorld.com, 03/01/98
- Digg
- Reddit
- SlashDot
- Stumble
- del.icio.us
- Technorati
- dzone
This
Java In Depth column will describe the issues that I uncovered in my attempt to develop a thread-safe collection. A collection is called
"thread-safe" when it can be used safely by multiple clients (threads) at the same time. "So what's the problem?" you ask.
The problem is that, in typical usage, a program both changes a collection (called
mutating), and reads it (called
enumerating).
Some people simply do not register the statement, "The Java platform is multithreaded." Sure, they hear it, and they nod their
heads. But they don't grasp that, unlike C or C++, in which threading was bolted on from the side through the OS, threads
in Java are basic language constructs. This misunderstanding, or poor grasp, of the inherently threaded nature of Java, inevitably
leads to two common flaws in programmers' Java code: Either they fail to declare a method as synchronized that should be (because
the object is in an inconsistent state during the method's execution) or they declare a method as synchronized in order to
protect it, which causes the rest of the system to operate inefficiently.
I came across this problem when I wanted a collection that multiple threads could use without needlessly blocking the execution of the other threads. None of the collection classes
in the 1.1 version of the JDK are thread-safe. Specifically, none of the collection classes will allow you to enumerate with
one thread while mutating with another.
Non-thread-safe collections
My basic problem was as follows: Assuming you have an ordered collection of objects, design a Java class such that a thread
can enumerate all or part of the collection without worrying about the enumeration becoming invalid due to other threads that
are changing the collection. As an example of the problem, consider Java's Vector class. This class is not thread-safe and causes many problems for new Java programmers when they combine it with a multithreaded
program.
The Vector class provides a very useful facility for Java programmers: namely, a dynamically-sized array of objects. In practice, you
might use this facility to store results where the final number of objects you will be dealing with isn't known until you
are done with them all. I constructed the following example to demonstrate this concept.
01 import java.util.Vector;
02 import java.util.Enumeration;
03 public class Demo {
04 public static void main(String args[]) {
05 Vector digits = new Vector();
06 int result = 0;
07
08 if (args.length == 0) {
09 System.out.println("Usage is java demo 12345");
10 System.exit(1);
11 }
12
13 for (int i = 0; i < args[0].length(); i++) {
14 char c = args[0].charAt(i);
15 if ((c >= '0') && (c <= '9'))
16 digits.addElement(new Integer(c - '0'));
17 else
18 break;
19 }
20 System.out.println("There are "+digits.size()+" digits.");
21 for (Enumeration e = digits.elements(); e.hasMoreElements();) {
22 result = result * 10 + ((Integer) e.nextElement()).intValue();
23 }
24 System.out.println(args[0]+" = "+result);
25 System.exit(0);
26 }
27 }
The simple class above uses a Vector object to collect digit characters from a string. The collection is then enumerated to compute the integer value of the string.
There is nothing wrong with this class except that it is not thread-safe. If another thread happened to hold a reference to
the digits vector, and that thread inserted a new character into the vector, the results of the loop in lines 21 through 23 above would
be unpredictable. If the insertion occurred before the enumeration object had passed the insertion point, the thread computing
result would process the new character. If the insertion happened after the enumeration had passed the insertion point, the loop
would not process the character. The worst-case scenario is that the loop might throw a NoSuchElementException if the internal list was compromised.
- Digg
- Reddit
- SlashDot
- Stumble
- del.icio.us
- Technorati
- dzone
Resources
- Generically Speaking -- a column on designing collections from December 1996 http://www.javaworld.com/jw-12-1996/jw-12-indepth.html
- Java's new Collections Framework for 1.2 http://java.sun.com/products/jdk/1.2/docs/guide/collections/index.html
- Concurrent Programming in Java by Doug Lea, published by Addison-Wesley ISBN 0-201-69581-2 -- An excellent source of information on issues surrounding the
use of multiple threads in your Java applets and applications.
- Design Patterns Elements of Reusable Object-Oriented Software by Gamma et al, published by Addison-Wesley, ISBN 0-201-63361-2 -- This book shows how valuable the separation of "what"
and object does and "how" a particular class design does it.
- "An in-depth look at Java's character type"
Eight (bits) is not enough -- Java's character type adds another eight. http://www.javaworld.com/javaworld/jw-01-1998/jw-01-indepth.html
- "A first look at Borland's JBuilder IDE" http://www.javaworld.com/jw-11-1997/jw-11-indepth.html
- "A look at inner classes"
Reduce class clutter in your Java designsUse inner classes. http://www.javaworld.com/javaworld/jw-10-1997/jw-10-indepth.html
- "Take an in-depth look at the Java Reflection API"
Learn about the new Java 1.1 tools for finding out information about classes. http://www.javaworld.com/javaworld/jw-09-1997/jw-09-indepth.html
- "Take a look inside Java classes"
Learn to deduce properties of a Java class from inside a Java program. http://www.javaworld.com/javaworld/jw-08-1997/jw-08-indepth.html
- "Build an interpreter in Java -- Implement the execution engine"
Here's how to take the interpreter classes and run with them. http://www.javaworld.com/javaworld/jw-07-1997/jw-07-indepth.html
- "How to build an interpreter in Java, Part 2The structure"
The trick to assembling the foundation classes for a simple interpreter. http://www.javaworld.com/javaworld/jw-06-1997/jw-06-indepth.html
- "How to build an interpreter in Java, Part 1The BASICs"
For complex applications requiring a scripting language, Java can be used to implement the interpreter, adding scripting
abilities to any Java app. http://www.javaworld.com/javaworld/jw-05-1997/jw-05-indepth.html
- "Lexical analysis, Part 2Build an application"
How to use the StreamTokenizer object to implement an interactive calculator. http://www.javaworld.com/javaworld/jw-02-1997/jw-02-indepth.html
- "Lexical analysis and JavaPart 1"
Learn how to convert human-readable text into machine-readable data using the StringTokenizer and StreamTokenizer classes.
http://www.javaworld.com/javaworld/jw-01-1997/jw-01-indepth.html
- "Code reuse and object-oriented systems"
Use a helper class to enforce dynamic behavior. http://www.javaworld.com/javaworld/jw-12-1996/jw-12-indepth.html
- "Container support for objects in Java 1.0.2"
Organizing objects is easy when you put them into containers. This article walks you through the design and implementation
of a container. http://www.javaworld.com/javaworld/jw-11-1996/jw-11-indepth.html
- "The basics of Java class loaders"
The fundamentals of this key component of the Java architecture. http://www.javaworld.com/javaworld/jw-10-1996/jw-10-indepth.html
- "Not using garbage collection"
Minimize heap thrashing in your Java programs. http://www.javaworld.com/javaworld/jw-09-1996/jw-09-indepth.html
- "Threads and applets and visual controls"
This final part of the series explores reading multiple data channels. http://www.javaworld.com/javaworld/jw-07-1996/jw-07-mcmanis.html
- "Using communication channels in applets, Part 3"
Develop Visual Basic-style techniques to applet design -- and convert temperatures in the process. http://www.javaworld.com/javaworld/jw-06-1996/jw-06-mcmanis.html
- "Synchronizing threads in Java, Part 2"
Learn how to write a data channel class, and then create a simple example application that illustrates a real-world implementation
of the class. http://www.javaworld.com/javaworld/jw-05-1996/jw-05-mcmanis.html
- "Synchronizing threads in Java"
Former Java team developer Chuck McManis walks you through a simple example illustrating how to synchronize threads to assure
reliable and predictable applet behavior. http://www.javaworld.com/javaworld/jw-04-1996/jw-04-synch.html