Letters to the Editor

Multithreaded Programming

'Can double-checked locking be fixed?'

Brian Goetz

Finally, a fix for DCL! But how does it perform?

Brian

Here is the correct double-checked locking (DCL) for Java:

  class Singleton {
    private static Singleton theInstance;
    private static final ThreadLocal perThreadInstance =
      new ThreadLocal() {
          public Object initialValue() { return createInstance(); }
        };
    public static Singleton getInstance() {
      // First check -- inside ThreadLocal.get()
      return (Singleton)perThreadInstance.get();
    }
    private static synchronized Singleton createInstance() {
      // Double check
      if (theInstance == null)
        theInstance = new Singleton();
      return theInstance;
    }
  }

Please see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html#ThreadLocal for more information.

Alexander

Alexander, I was expecting to hear from you on this subject. I too discovered this technique a while ago and recently saw the update on Bill Pugh's Java Memory Model page discussing it and crediting you as the source. However, in my testing (Sun or IBM JDK 1.3 on a two-processor Linux/Intel box), I found that

ThreadLocal.get()

was measurably slower than an uncontended synchronize, and Doug Lea's book (

Concurrent Programming in Java, Second Edition,

(Addison-Wesley, 1999; ISBN: 0201310090)) says the same. For that reason, I refrained from mentioning it in my article as a fix; while it solves the safety problem, it doesn't deliver the performance benefit that DCL was supposed to deliver. In fact, under many conditions, it's worse. Lea's data shows that

ThreadLocal

is indeed faster under contended access, and the more heavily contended, the better it performs by comparison. But for uncontended access, it's still slower.

ThreadLocal.get()

is just slow. Doug says it will be significantly faster in 1.4. Below is a test program I used to test the comparative performance of contended or uncontended access with synchronization, with

ThreadLocal

, and with nothing (unsafe access):

TlTest.java, Timer.java
import java.util.*;
public class TlTest {
  static class Singleton {
    private static Object o;
    public static synchronized Object getSomething() {
      if (o == null) 
        o = new Object();
      return o;
    }
  }
  static class NSingleton {
    private static Object o;
    public static Object getSomething() {
      if (o == null) 
        o = new Object();
      return o;
    }
  }
  static class TlSingleton {
    private static ThreadLocal tl = new ThreadLocal();
    public static Object getSomething() {
      Object o = tl.get();
      if (o == null) {
        synchronized (TlSingleton.class) {
          o = Singleton.getSomething();
        }
        tl.set(o);
      }
      return o;
    }
  }
  static int iterations=200000;
  static int nThreads = 5;
  static class SThread extends Thread {
    public void run()  {
      for (int i=0; i<iterations; i++) {
        Object o = Singleton.getSomething();
      }
    }
  }
  static class TThread extends Thread {
    public void run()  {
      for (int i=0; i<iterations; i++) {
        Object o = TlSingleton.getSomething();
      }
    }
  }
  static class NThread extends Thread {
    public void run()  {
      for (int i=0; i<iterations; i++) {
        Object o = NSingleton.getSomething();
      }
    }
  }
  public static void main(String[] args) throws Exception {
    Timer t = new Timer();
    if (args.length >= 2) 
      nThreads = Integer.parseInt(args[1]);
    if (args.length >= 3) 
      iterations = Integer.parseInt(args[2]);
    Thread threads[] = new Thread[nThreads];
    for (int i=0; i<nThreads; i++) {
      if (args[0].equals("S"))
        threads[i] = new SThread();
      else if (args[0].equals("N"))
        threads[i] = new NThread();
      else if (args[0].equals("T"))
        threads[i] = new TThread();
    }
    t.start();
    for (int i=0; i<nThreads; i++) 
      threads[i].start();
    for (int i=0; i<nThreads; i++) 
      threads[i].join();
    t.stop();
    double sec = t.getTime() / 1000.0;
    System.out.println("Total time: " + sec + "s" 
                       + "    (" + nThreads + " threads, " + iterations + 
" iterations)");
  }
}
public class Timer {
  long startTime, endTime, lastPauseTime, totalPauseTime;
  boolean running = false, paused = false;
  public Timer() {
  }
  public void start() {
    startTime = System.currentTimeMillis();
    running = true;
  }
  public void stop() {
    endTime = System.currentTimeMillis();
    running = false;
  }
  public void pause() {
    lastPauseTime = System.currentTimeMillis();
    paused = true;
  }
  public void resume() {
    if (paused) {
      long now = System.currentTimeMillis();
      totalPauseTime += (now-lastPauseTime);
      paused = false;
    };
  }
  public long getTime() {
    if (running) 
      return System.currentTimeMillis() - startTime - totalPauseTime;
    else
      return endTime - startTime - totalPauseTime;
  }
}

Invoke it as:

  TlTest {S|N|T} <nThreads> <nIterations>

Using

nThreads=1

shows the cost of uncontended synchronization. The performance difference between

nThreads=1

and

nThreads=2

is enough that if there is any significant amount of contention,

ThreadLocal

starts to win out.

ThreadLocal

is a sensible technique to use and a useful addition to the class library (albeit poorly implemented) that doesn't get enough attention. Brian Goetz

Java Theory

Encapsulation is not information hiding'

Wm. Paul Rogers

Authors hash out information hiding

Wm. Paul, You, Jeff Mather [author of "Java Tip 107: Maximize Your Code Reusability," (JavaWorld)], and Allen Holub [author of "Building User Interfaces for Object-Oriented Systems, Part 1," (JavaWorld, July 1999)] really need to get together and hash all of this out. Three different authors, three different perspectives, all on JavaWorld. I would love to see an article that possibly resolves these perspectives (hint!):

  • Your article: the more typical approach to information hiding/encapsulation
  • Mather's article: the less conventional approach -- split stateless methods into static methods in sister class to decouple from instances, which aids inheritance
  • Holub's article: get and set methods are an evil excuse for encapsulation (pardon the loose paraphrase)

A more specific comment: You change

Route

to retrieve

Position

object by index using (effectively) a clone, which is a change from returning an array. Wouldn't it be better to return an

Iterator

of cloned

Positions

? That way, it is easy to convert the internal representation (array,

ArrayList

, or

Vector

) into a highly recognizable idiom for working with a list of items. Clark

[Note: Allen Holub's responses are in red, Wm. Paul Roger's are in green, and Jeff Mather's are in blue.]

Clark et al., I suppose there's room for discussion here, but I find Jeff's notions of making classes all Booch utilities to be abhorrent (or at least not object-oriented). I've no argument with the idea that you should use interfaces as much as possible, but if the methods are stand-alone, then you simply don't have an object system anymore, you have a bunch of global methods. The real debate, I suppose, is whether objects are good or not -- and I come down on the side of objects. But don't think that you have an object-oriented system if you have nothing but stateless objects calling static methods -- that's just an inconvenient way to write a procedural program. On the other hand, I think that Wm. Paul's article is quite sound, though as you point out, I'd take it a step further and eliminate the get/set methods -- you don't actually need them. Generally, get/set methods appear in classes designed out of context. That is, if you design a dynamic model based on use-case analysis and then implement only those methods required by the dynamic model, you tend to not have much in the way of get/set methods. The get/set methods appear when you don't know exactly how a class might be used, so you throw them in to make them more generic. Allen Holub

Clark, The perspective of information hiding in my article aligns closely with Allen's articles. In particular, I think each of our articles emphasizes information hiding as a design principle. Allen does boldly state that getter/setter methods are evil, but I understand that to mean that those methods indicate that a class probably has been designed outside a specific domain problem. I have no objection to that notion. Rebecca Wirfs-Brock and Brian Wilderson discuss a fundamental principle driving design decisions in "Object-Oriented Design: A Responsibility-Driven Approach" (SIGPLAN Notices, Vol. 24, No. 10, October 1989): responsibility-driven design specifies object behavior rather than object structure. Also, I think Allen's argument against getter/setter methods is that a class designed for a specific domain will often not need them. That falls in line with my statement that a class should encapsulate no more or less than a comprehensive set of reasonable responsibilities. What is reasonable? Let the domain be the guide! Is it reasonable that there will not be getter/setter methods? Absolutely. I readily admit that the class development in my article is not firmly grounded in a specific domain problem, and Allen points out that this often leads to generic solutions that include those rascally getter/setter methods. My article does not align well with Jeff's. I don't feel the procedural makeup of his methods are either necessary or advantageous. I specifically (though briefly) address the semantic advantage of currying a procedural-style function into an object-oriented call. And it seems to me that passing object references into globally visible static methods misses the power of polymorphism. Finally, addressing your "more specific comment." Note that I did indicate the use of a copy constructor rather than a specific clone. You've kindly called this "effectively a clone." It was actually a slip on my part. As I mentioned in the article, class Route is far from complete. I suspect when the actual responsibilities of Route are closely examined, an iterator over the Position objects would be likely, and a method directly accessing Position objects by index probably would not be warranted. Wm. Paul Rogers

Gentlemen,

I'm glad to see that there is consensus on the get/set issue. I will say that many of the developers I know do not accept this principle -- for better or for worse. I once trotted out Allen's series of articles that covered it. The principle was rejected as just an extreme view -- as opposed to treating the principle as an ideal to strive towards. Personally, I can see the value but am not sure how to achieve it. Maybe it would be a good topic for a future article from Wm. Paul giving his spin on it in alignment with Allen's previous series.

Thanks for your interest,

Clark

Clark et al., The best way that I know to eliminate the get/set methods is to go through the design process, rather than throw together classes in an ad-hoc way. That is, start out with English problem statements, do use-case analysis, develop a dynamic model from the workflow in the use case, then (as the last step) capture the structure of the objects into a static model. Most programmers start with the static model, which is really working in the dark since you are just guessing about how the objects will be used. I'm in the middle of a series on the design process for IBM developerWorks, and you might want to read through that it. It's indexed at http://www.holub.com/aiharticles.html. Allen Holub

Clark, I think the techniques presented in my article are complementary with those presented in Wm. Paul's. I agree with "Place data and the operations that perform on that data in the same class," but I would advise that you move the bulk of the code within those operations out to globally visible static methods to make that code easier to reuse. Doing so also tends to clean up the code to some degree, and makes it easier to debug since its total I/O interface is far easier to ascertain. I wish I had mentioned what Wm. Paul said under "Information hiding rule 3: Don't expose a class's internal structure" in my Java Tip, because it is a concrete application of my article's Step 3, which I use all the time to enable reuse. I don't subscribe to Allen's prohibition on the use of getter and setter methods. What he's essentially saying is that objects shouldn't share information, which is contrary to what goes on in the real world, which objects are supposed to model. Only one object should be responsible for maintaining the value of any particular piece of data, but clients should be allowed to know that value where appropriate. Trying to design Allen's way takes defensive programming to an extreme where it is a giant headache to get anything meaningful done. Jeff

1 2 Page 1
Page 1 of 2