Question

Sorry for the non-technical title, but I think it summarizes my question well. If I interpret what I've read correctly, the synchronized block (apart from other consequences) will make all variables updated to/from the main memory (even those which are not accessed explicitly inside the synchronized block, only their "parent"?). E.g. quoting the answer of this stackoverflow question (I took it out of context, I'll return to it later):

The memory barrier applies to all memory references, even unrelated ones.

I need confirmation whether I interpret this correctly. I've 2 threads (threadA, threadB). Consider the following code:

public class SomeClass {

private final Object mLock = new Object();
private int[] anArray;

public void initA() {
  synchronized(mLock) {
     ...
     anArray = new int[...];
     operationA();
  }
}

public void operationA() {
  synchronized(mLock) {
      // Manipulating the ELEMENTS of anArray,
      // e.g. in loops, etc.
      anArray[i] = ...
  }
}

public int[] getterB() {
   synchronized(mLock) {
      return anArray;   
   }
}
}

getterB() is called from ThreadB, initA() and operationA() are called from ThreadA. (Note that initA() is called even before ThreadB is created, so only getterB() and operationA() are concurrent.) Also note that I've a good reason not to return a copy of the array in getterB() (no, threadB doesn't want to change its elements; the reason is an external requirement for my software that is not relevant now).

threadB does this:

int[] anArray = aSomeClass.getterB(); // aSomeClass is an instance of SomeClass
if (anArray[i] == n) { ....... } // various operations
...
//  various other operations that read the elements of anArray

As you can see, in getterB(), only the anArray reference is accessed within the memory barriers, and not the array values themselves. My questions:

  1. Will threadB see the most up-to-date array element values? (i.e. are the elements themselves updated too from main memory in getterB()?)

  2. The quoted statement mentioned that unrelated cached copies are updated from main memory too. I am not 100% how to intepret this unrelated (unrelated to the variable used for locking? or unrelated to the entire synchronized block?). I know I took the quote out of context, and since it's a different stackoverflow question, I've added a comment there. So I appreciate if that question of mine is answered there (or here -- I don't care).

  3. Is there any difference in the answer if anArray is an array of Objects (and not of primitive types)? Going even further, what if it's not an array, but a class that contains references to other classes? (i.e. an object that refers to other objects, and I access the contained objects through the object returned by getterB()). Will threadB use up-to-date copies of these contained references, or may it use its own local cached copies (since getterB() only updated their container object, but not the contained references themselves)?.

Was it helpful?

Solution

Taking your questions in order:

  1. Yes: You can safely assume that all of the values modified from any previously-called operationA() will be "up-to-date" in the array referenced by the result of getterB().

  2. I'll let that be answered in the other link; I confess that I did not read that link yet. But my understanding is that all pending memory write-backs will "effectively" happen as you both enter and exit a synchronized block (although the details of how this happens -- i.e., the efficiency of this, and whether there are more caching/pipelining "tricks" going on to make it appear that way -- will depend on the hardware and compiler). If you want more details about this, I once found this link useful: http://www.infoq.com/articles/memory_barriers_jvm_concurrency

  3. No, there's no difference (given what I wrote in answer 2).

Finally, just a comment that I would be very wary of your code as summarized above because of the fact that getterB() does not return a copy of the array. I understand that you have your reasons for doing it in the above way (and that you didn't want this sort of feedback!), but you should be sure to understand that all of the "various operations" in thread B on anArray that occur after getterB() has returned will not be protected. (In other words, there will be hazards with any changes to the array made on thread A during this time.) One alternative that would avoid an inefficient deep array copy is to move these "various operations" within a synchronized block within a new method in SomeClass and get rid of getterB() entirely. Of course, I realize that the "right solution" in your code is dependent on many things not shown here, so feel free to ignore this bit.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top