1

This is what my code currently looks like:

private boolean[] isInitialized = new boolean[COUNT];

private void ensureInitialized(int i) {
    if (! isInitialized[i]) {
        initialize(i);
        isInitialized[i] = true;
    }
}

Now I want to have it thread-safe. I know that double-checked-locking in Java is "teh 3vilness!!1", but since ensureInitialized may be called very often, I don't want it to be synchronized. So I am thinking of doing this:

private boolean[] isInitialized = new boolean[COUNT];

private void ensureInitialized(int i) {
    if (! isInitialized[i]) {
        synchronized (this) {
            if (! isInitialized[i]) {
                initialize(i);
                isInitialized[i] = true;
            }
        }
    }
}

Now what do I have to do to make this actually thread safe?
Some subquestions:

  • Making isInitialized volatile is not necessary, since the variable is not changed, right?
  • The array elements are changed, how can I make those volatile?
  • Are there generally better ways to do this?

(Also notice that this is an instance method, so static initializer will no work)

Cephalopod
  • 14,632
  • 7
  • 51
  • 70

2 Answers2

3

You could also consider using Suppliers.memoize(Supplier<T> delegate) from guava library.

mindas
  • 26,463
  • 15
  • 97
  • 154
  • I like this kind of answer: double checked locking is such an atrocity ! I don't want to see any bit of it im my code :) – Guillaume Feb 03 '11 at 16:08
  • I second that. After I found this idiom I have never looked back. The advantage of this idiom is that library guys will always try to gravitate towards the best approach and if they find one that is better, they will change it and you don't have to rewrite your thousand double-checked blocks across your code base. – mindas Feb 03 '11 at 16:47
1

Please note that java implementation of double-checking is called "broken pattern" because it was proven as fail (for example see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html). To work around just use atomic operation. Follow is sample how to build thread safe singleton:

static AtomicReferenceArray<Boolean> instance = 
   new AtomicReferenceArray<Boolean>(COUNT);

private void ensure(int i)
{
    if(!instance.get(i) )
    {
        synchronized(this){
        if( !instance.get(i) ){
           initialize(i);
           instance.set(i, true);
        }
    }
}
Dewfy
  • 23,277
  • 13
  • 73
  • 121
  • Is there any evidence that java's synchronization is unnecessarily slow? In other words, yes, it is slow when there is contention, but when there is no contention it's fast. – President James K. Polk Oct 09 '10 at 18:11
  • +1, but an explanation is due. The double-checking lock is _not_ broken since Java 1.5 - using the `volatile` keyword makes it work fine. However, as the OP rightly assumed, the reads and writes to elements of the array are not volatile. What `AtomicXArray` classes do is do a volatile read for each element (See the source - `unsafe.getObjectVolatile()`). – Bozho Oct 09 '10 at 18:16
  • -1, Double check locking is not broken. The rest of the answer is technically correct. Maybe less preaching and more technical solutions? – Tim Bender Oct 10 '10 at 05:12
  • @Tim Bender - just go to link and/or read comment of @Bozho. Or at least try to google around phrase "java double checking" "broken pattern" – Dewfy Oct 10 '10 at 06:55
  • @GregS - it is rather funny, why we should use synchronization at all if no "contention"? When you sure that singleton used only by single thread lazy-load pattern is enough. – Dewfy Oct 10 '10 at 07:26
  • @Bozho - by the way assume following code **volatile boolean[] inst** this mean that volatile affects assigning of variable instance, but doesn't talks that access to inst[0] is also used volatile rules – Dewfy Oct 10 '10 at 12:54