4

Many questions/answers have indicated that if a class object has a final field and no reference to it is exposed to any other thread during construction, then all threads are guaranteed to see the value written to the field once the constructor completes. They have also indicated that storing into a final field a reference to a mutable object which has never been accessed by outside threads will ensure that all mutations which have been made to the object prior to the store will be visible on all threads which access the object via the field. Unfortunately, neither guarantee applies to writes of non-final fields.

A question I do not see answered, however, is this: If the semantics of a class are such that a field cannot be final, but one wishes to ensure the "publication" of the field and the object identified thereby, what is the most efficient way of doing that? As an example, consider

class ShareableDataHolder<T>
{
  Object data; // Always identifies either a T or a SharedDataHolder<T>
}
private class SharedDataHolder<T> extends ShareableDataHolder<T>
{
  Object data; // Always identifies either a T or a lower-numbered SharedDataHolder<T>
  final long seq; // Immutable; necessarily unique
}

The intention would be that data will initially identify a data object directly, but that it could legitimately at any time be changed to identify a SharedDataHolder<T> which directly or indirectly encapsulates an equivalent data object. Assume all code is written to work correctly (though not necessarily optimally-efficiently) if any read of data may arbitrarily return any value that was ever written to data, but may fail if it reads null.

Declaring volatile Object data would be semantically correct, but would likely impose extra costs on every subsequent access to the field. Entering a dummy lock after initially setting the field would work, but would be needlessly slow. Having a dummy final field, which the object sets to identify itself would seem like it should work; although technically I think it might require that all accesses to the other field be done through the other field, I can't see any realistic scenario where that would matter. In any case, having a dummy field whose purpose is only to provide the appropriate synchronization via its existence would seem wasteful.

Is there any clean way to inform the compiler that a particular write to data within the constructor should have a happens-before relationship with regard to any reads of that field which occur after the constructor returns (as would be the case if the field were final), without having to pay the costs associated with volatile, locks, etc.? Alternatively, if a thread were to read data and find it null, could it somehow repeat the read in such a fashion as to establish a "happens after" with regard to the write of data [recognizing that such a request might be slow, but shouldn't need to happen very often]?

PS--If happens-before relationships are non-transitive, would a proper happens-before relationship exist in the following scenario?

  1. Thread 1 writes to a non-final field dat in some object Fred and stores a reference to it into to a final field George.
  2. Thread 2 copies the reference from George into a non-final field Larry.
  3. Thread 3 reads Larry.dat.

From what I can tell, a happens-before relationship exists between the write of Fred's field dat and a read of George. Would a happens-before relationship exist between the the write of Fred's dat and a read of Larry that returns a reference to Fred that was copied from a final reference to Fred? If not, is there any "safe" way to copy a reference contained in a final field to a non-final field that would be accessible via other threads?

PPS--If an object and its constituents are never accessed outside their creation thread until the main constructor finishes, and the last step of the main constructor is to stores within the main object a final reference to itself, is there any "plausible" implementation/scenario where another thread could see a partially-constructed object, whether or not anything actually uses that final reference?

supercat
  • 77,689
  • 9
  • 166
  • 211
  • 3
    " They have also indicated that storing into a final field a reference to a mutable object which has never been accessed by outside threads will ensure that all mutations which have been made to the object prior to the store will be visible on all threads which access the object via the field" <-- uh, really? I must misunderstand something here. Have a link? – fge Mar 21 '14 at 17:12
  • 1
    Also: "Declaring volatile Object data would be semantically correct, but would likely impose extra costs on every subsequent access to the field." <-- This is environment-specific. On x86, as far as I understand, all of the cost is on the write; the reads aren't more expensive than non-`volatile` fields. [More info](http://stackoverflow.com/questions/1090311/are-volatile-variable-reads-as-fast-as-normal-reads) – yshavit Mar 21 '14 at 17:15
  • 1
    The problem is that 2 unrelated features -- un-reassignable, visibility -- are coupled unnecessarily by the same keyword `final`. – ZhongYu Sep 29 '15 at 23:38

2 Answers2

8

Short answer

No.

Longer answer

JLS 17.4.5 lists all* of the ways of establishing a happens-before relationship, other than the special case of final field semantics:

  1. An unlock on a monitor happens-before every subsequent lock on that monitor.
  2. A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
  3. A call to start() on a thread happens-before any actions in the started thread.
  4. All actions in a thread happen-before any other thread successfully returns from a join() on that thread.
  5. The default initialization of any object happens-before any other actions (other than default-writes) of a program.

(The original lists them as bullet points; I'm changing them to numbers for convenience here.)

Now, you've ruled out locks (#1) and volatile fields (#2). Rules #3 and #4 relate to the life-cycle of the thread, which you don't mention in your question, and doesn't sound like it would apply. Rule #5 doesn't give you any non-null values, so it doesn't apply either.

So of the five possible methods for establishing happens-before, other than final field semantics, three don't apply and two you've explicitly ruled out.


* The rules listed in 17.4.5 are actually consequences of the synchronization order rules defined in 17.4.4, but those relate pretty directly to the ones mentioned in 17.4.5. I mention that because 17.4.5's list can be interpreted as being illustrative and thus non-exhaustive, but 17.4.4's list is non-illustrative and exhaustive, and you can make the same analysis from that directly, if you don't want to rely on the intermediate analysis that 17.4.5 provides.

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • If the last step of a constructor causes a reference to the constructed object to be stored into a final field of some object, would that "globally" establish the ordering of everything written to the object prior to that time, or would that only establish ordering with respect to code that actually read that particular `final` field? What would be the most reasonable implementation of the aforementioned class if the semantics are as stated? Maybe `volatile` would be the best approach, but it seems ugly for code to demand a much stronger guarantee from the JVM than what it needs. – supercat Mar 21 '14 at 17:43
  • The hb ordering is only with respect to reading that particular `final` field. There was talk of adding a fences API to Java that would provide the more fine-tuned semantics you're referring to, but that didn't make it in. – yshavit Mar 21 '14 at 17:48
  • If a thread reads the contents of a `final` field and copies it to a non-final field, do happens-before relationships that were applicable to the `final` field also applicable to the copied field? Otherwise, what would you think of the idea of having two `data` fields, one volatile and one not? For many read operations, the costs associated with stale data would be less than the costs of having the CPU ensure that data was up-to-date, but if code which read `null` from the non-volatile pointer were to read the volatile pointer, correctness would be assured. – supercat Mar 21 '14 at 23:09
  • 1
    Another approach would be to have each object use a `final` field for its supplied data, separate from the one that identifies the shared-data link. Doing that, however, would forfeit the ability to discard redundant information. – supercat Mar 21 '14 at 23:12
  • There's no transitivity applied to the hb-relationship with `final` fields, so I don't think the copying approach wouldn't work; reorderings could give you weird results. The two-reference approach would work, but you'd need to make sure that the object in question is thread-safe even in the absence of synchronization -- which basically means everything in that object has to be `final`. – yshavit Mar 22 '14 at 00:13
  • The intended semantics would be that code would behave correctly (though not necessarily optimally) if any read of a field may arbitrarily read any value which has ever been written to that field (all fields are written in the constructor); the *only* problem scenario would be if a field erroneously read as blank. Given such semantics, requiring the level of synchronization implied by `volatile` seems massively overkill (the linked article suggests that the cost of volatile reads, even on x86, are non-trivial). – supercat Mar 22 '14 at 17:50
  • If relationships do not imply transitivity, can there be dangers if references stored in `final` fields are stored to non-final fields [see edit to question for an example]? – supercat Mar 22 '14 at 18:02
  • Sorry, I'll read in more detail in a bit and answer you better... but for now, let me clarify that most hb relationships are transitive, just not the `final` field semantics. So if thread 1 does action `a` and then creates object `B` with final field `b`, and then thread 2 reads reads that field `b`, then thread 2 _is_ guaranteed to see `b` fully-formed, but it's _not_ guaranteed to see action `a`. If `b` were `volatile` instead of `final`, then thread 2 _would_ be required to see `a` due to transitive hb relationships. – yshavit Mar 23 '14 at 01:13
  • It would be nice to know what is and isn't guaranteed; I would think there are a fair number of immutable types (including strings, big numbers, etc.) where many independently-created instances, including some rather large ones, will happen to be identical. For e.g. code which creates each new string to check whether a matching string was interned would be expensive, but if a string is found to be equal to an existing interned string, having the non-interned string abandon its backing array in favor of the interned one should allow a cheap optimization for both memory usage... – supercat Mar 23 '14 at 16:43
  • No sense repeating why I'd like to be able to use a "plain" field to hold changeable references to interchangeable objects. I guess my main point is that I see it a pattern which could, in the absence of a typical-read-access speed penalty, have minimal cost, common slight rewards, and occasional great rewards, but incurring a penalty on every read access would be undesirable. – supercat Mar 23 '14 at 16:52
  • I don't disagree with you that finer-tuned memory semantics would be a nice thing. But they're not there. :) – yshavit Mar 23 '14 at 17:30
  • Well, let me know what you find out about scenarios like I described. If behavior is defined in those cases, it would be helpful to know what about them makes them so; if it is not defined, that would suggest a source of immutable-object-related Heisenbugs. – supercat Mar 23 '14 at 22:12
  • According to http://cs.oswego.edu/pipermail/concurrency-interest/2013-November/011954.html using volatile doesn't even work! – Archie Aug 08 '16 at 22:12
0

You can apply final field semantics without making the fields of your class final but by passing your reference through another final field. For this purpose, you need to define a publisher class:

class Publisher<T> {
  private final T value;
  private Publisher(T value) { this.value = value; }
  public static <S> S publish(S value) { return new Publisher<S>(value).value; }
}

If you are now working with an instance of ShareableDataHolder<T>, you can publish the instance by:

ShareableDataHolder<T> holder = new ShareableDataHolder<T>();
// set field values
holder = Publisher.publish(holder);
// Passing holder to other threads is now safe

This approach is tested and benchmarked and turns out to be the most performant alternative on current VMs. The overhead is minimal as escape analysis typically removes the allocation of the very short-lived Publisher instance.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192