Cheapest way of establishing happens-before with non-final field

眉间皱痕 提交于 2019-12-01 06:44:13

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!