4

I was reading the ArrayBlockingQueue implementation code another day by Doug Lea and noticed a lot of methods (public, default, and private) have the following references:

final Object[] items = this.items;
final ReentrantLock lock = this.lock;

I have asked around to have a reasonable explanation but so far no satisfactory answers. I am not quite sure why we need to have such local variables in the first place? And what is the benefit(s) of coding this way?

Maybe I missed some important points in concurrent programming. Could you please help to shed some lights on this?

victor yao
  • 89
  • 6
  • Welcome to Stack Overflow! Please take the [tour] (you get a badge!), have a look around, and read through the [help], in particular [*How do I ask a good question?*](/help/how-to-ask) I also recommend Jon Skeet's [Writing the Perfect Question](https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/) It would help people help you **a lot** if you **showed us** examples of what you're asking about. – T.J. Crowder Feb 01 '19 at 14:37
  • 1
    For those of us who are unfamiliar with Doug Lee, could you give a link to his example code, or paste some of his example code here. – tgolisch Feb 01 '19 at 14:59
  • 1
    @tgolisch I edited the question with a link to an OpenJDK mirror that has the referenced class' code. – Jiri Tousek Feb 01 '19 at 16:58
  • @tgolisch Sorry. I misspelled his name: Doug Lea, who is the main author of the standard Java library, especially for multithreading and concurrency packages. The class I mentioned is one of the core java classes on Java5 package "java.util.concurrent". Thanks Jiri for adding the link to the source code. – victor yao Feb 01 '19 at 16:59
  • My guess is that those variables are volatile, and storing them in a local variable means you only do a volatile read once instead of every time you use the variable. I think this is what John Bollinger is explaining in a much more detailed technical way. – NickL Feb 01 '19 at 17:47
  • @NickL they are not volatiles. They are final class variables: final Object[] items; final ReentrantLock lock; – victor yao Feb 01 '19 at 18:01
  • Ah I see, my guess was wrong. Now I'm also curious why one would do that if they are not volatiles.. – NickL Feb 02 '19 at 10:06

3 Answers3

3

A very good reason for a method to set a local variable to the value of an accessible class or instance variable, or a value accessible through one of those, is to thereafter be independent of any modifications to that variable by other threads. With some caveats, this allows the method that needs to access that value more than once to perform a consistent computation reflecting the state of the host object at some specific point in time, even if that state has changed by the time the result is returned. This is likely what's happening in the code you have been studying.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    John, many thanks for your explanation. I kind of understanding what you are talking about. But still a bit confusion from my side as the referenced class variables are final and cannot be reassinged, for example: final Object[] items = this.items; The above local variable “items” just a pointer to “this.items” and any modifications to “this.items” are guarded by another local variable “lock”, which again references “this.lock”. final ReentrantLock lock = this.lock; – victor yao Feb 01 '19 at 17:34
  • Obviously more than one threads can at the same time access “this.items” and but only one thread can make changes at a time to the Object[] items. You could perform the same operation directly against “this.items”, without any issues (I guess), or not? Why again a local “final ReentrantLock lock” here? What kind of gain we have with this extra indirection? Could you please elaborate a bit more for me to have a better understanding? Very appreciated. – victor yao Feb 01 '19 at 17:34
  • @victoryao, in fact I cannot elaborate much more without analyzing the code. But the point would not be protecting against changes to the *elements* of `this.items`, but rather protecting against `this.items` itself being replaced with a reference to a different array. Supposing, then, that that may actually happen, and supposing further that `this.lock` is supposed to protect access to the members of that array, it is entirely plausible that the lock gets replaced when the items array does. That would be another part of the same overall plan, and it would explain copying of the lock ref. – John Bollinger Feb 01 '19 at 20:16
  • Yes I agree with you if `this.items` can be replace with a reference to a different array; same is true for `this.lock`. Both are declared as the final class variables: `final Object[] items; final ReentrantLock lock`. I believe that the final variables are not re-assignable. Or I misunderstood you about _rather protecting against this.items itself being replaced with a reference to a different array_? – victor yao Feb 02 '19 at 10:36
  • Surely suppose in a method you could do something like: `Object[] items = this.items; items = new Object[10];`. However this reassignment to `items` won’t affect `this.items`. Or do I get it wrong? Please correct me if this is not the case or some other ways which a final variable can be replaced? Anyway it is an interesting point. Thanks John. – victor yao Feb 02 '19 at 10:37
  • The point @victoryao, is that after one method does `Object[] items = this.items;`, that thread, or more likely *some other* one, may do `this.items = new Object[howManyItems];` or similar. – John Bollinger Feb 02 '19 at 12:49
  • This is my argument. You cannot reassign an new array to `this.items` directly. `this.items = new Object[howManyItems]; ` won't compile. Or you do as I showed previously but it won't affect the array that was initially assigned to `this.items` in the `ArrayBlockingQueue` constructor. So your concerned argument seems not valid in this case. – victor yao Feb 02 '19 at 16:50
2

It happened that I just came across this link which explained some of the main arguments of coding this way:[In ArrayBlockingQueue, why copy final member field into local final variable?. Please read it to understand more, instead, I am hoping of not getting more confused. I believe it helps you to look at this practice from another angle. It seems it at least met some of my curiosities around this coding style.

victor yao
  • 89
  • 6
  • There is another place talking about why use `final ReentrantLock lock = this.lock;`. [https://stackoverflow.com/questions/8089920/idiomatic-use-of-reentrantlock-in-concurrency-package?noredirect=1&lq=1] Please have a look to understand a bit more on the subject. – victor yao Feb 03 '19 at 17:36
  • Thanks for the answer, that clears things up for me. I think this answers your question doesn't it? You can accept your own answer. – NickL Feb 03 '19 at 21:28
  • @NickL yes indeed. I have added a summary as my final answer to the question. Thanks everyone for your interesting in the subject. – victor yao Feb 04 '19 at 10:11
2

After going through all relevant threads on the coding practice of assigning a final class variable to a local copy, i.e. a final class variable is never accessed directly from within a method, instead it is always referenced by a local variable reference:

final Object[] items = this.items;

final ReentrantLock lock = this.lock;

Typically you will find this code style in ArrayBlockingQueue and other concurrent classes

The following are my findings:

  • It is an idiomatic use, made popular by Doug Lea, the main author of the core Java library on multithreading/concurrency classes
  • The main consideration of this coding practice (or rather a hack) is for a small performance optimization back in Java 5 era
  • It is arguable if such a trick can have a performance gain; Some argued it is opposite with modern compiler; Others believe it is not needed

So my takings are that we should not be encouraged to adopt this practice. Because in many applications you don’t need it. Clean code maybe more important than a small performance gain; let alone no one is 100% certain whether this (a performance gain) is the case anymore.

victor yao
  • 89
  • 6