Boolean double-check idiom

Take the following java code:

public class SomeClass {
  private boolean initialized = false;
  private final List<String> someList; 

  public SomeClass() {
    someList = new ConcurrentLinkedQueue<String>();
  }

  public void doSomeProcessing() {
    // do some stuff...
    // check if the list has been initialized
    if (!initialized) {
      synchronized(this) {
        if (!initialized) {
          // invoke a webservice that takes a lot of time
          final List<String> wsResult = invokeWebService();
          someList.addAll(wsResult);
          initialized = true;
        }
      } 
    }
    // list is initialized        
    for (final String s : someList) {
      // do more stuff...
    }
  }
}

The trick is that it doSomeProcessingis called only under certain conditions. List initialization is a very expensive procedure and may not be necessary at all.

I read articles about why the double-check idiom is broken, and I was a bit skeptical when I saw this code. However, the control variable in this example is logical, so as far as I know, a simple write instruction is required.

, someList final , writes reads; ConcurrentLinkedQueue ArrayList LinkedList, final, writes - reads.

, ?

+3
5

, Java. 17.4.5 -before :

- . - , . x y, hb (x, y) , x y.

  • x y - y , hb (x, y).
  • - (ยง12.6) .
  • x - y, hb (x, y).
  • hb (x, y) hb (y, z), hb (x, z).

, , . , , .

:

, "", , "". , , , , .

if (!initialized)

initialized, , someList, , , .

,

, , someList final , writes reads

. , , , , , - . , ? , ? , , , , , .

+5

Wikipedia , volatile.

+4

ConcurrentLinkedQueue . javadoc :

, ConcurrentLinkedQueue ConcurrentLinkedQueue .

, :

// Thread 1
x = 42;
someList.add(someObject);

// Thread 2
if (someList.peek() == someObject) {
    System.out.println(x); // Guaranteed to be 42
}

, x = 42; someList.add(...). :

// Thread 1
someList.addAll(wsResult);
initialized = true;

// Thread 2
if (!initialized) { ... }
for (final String s : someList) { ... }

initialized = true; someList.addAll(wsResult);.

, - , volatile, .

+3

, , someList.isEmpty()?

0

-, . , . , , , , . impl .

volatile ArrayList<String> list = null;

public void doSomeProcessing() {
    // double checked locking on list
    ...

, :

static final String END_MARK = "some string that can never be a valid result";

final ConcurrentLinkedQueue<String> queue = new ...

public void doSomeProcessing() 
    if(!queue.contains(END_MARK)) // expensive to check!
         synchronized(this)
            if(!queue.contains(END_MARK))
                  result = ...
                  queue.addAll(result);
                  // happens-before contains(END_MARK)==true
                  queue.add( END_MARK );

     //when we are here, contains(END_MARK)==true

     for(String s : queue)
         // remember to ignore the last one, the END_MARK

, , - . - , List, " List impl, ", .

0

Source: https://habr.com/ru/post/1793264/


All Articles