public class Fences extends Object
volatile
or final
, using
instances of atomic classes, using synchronized
blocks or
methods, or using other synchronization facilities are not possible
or do not provide the desired control.
Memory Ordering. There are three methods for controlling
ordering relations among memory accesses (i.e., reads and
writes). Method orderWrites
is typically used to enforce
order between two writes, and orderAccesses
between a write
and a read. Method orderReads
is used to enforce order
between two reads with respect to other orderWrites
and/or
orderAccesses
invocations. The formally specified
properties of these methods described below provide
platform-independent guarantees that are honored by all levels of a
platform (compilers, systems, processors). The use of these
methods may result in the suppression of otherwise valid compiler
transformations and optimizations that could visibly violate the
specified orderings, and may or may not entail the use of
processor-level "memory barrier" instructions.
Each ordering method accepts a ref
argument, and
controls ordering among accesses with respect to this reference.
Invocations must be placed between accesses performed in
expression evaluations and assignment statements to control the
orderings of prior versus subsequent accesses appearing in program
order. These methods also return their arguments to simplify
correct usage in these contexts.
Usages of ordering methods almost always take one of the forms
illustrated in the examples below. These idioms arrange some of
the ordering properties associated with volatile
and
related language-based constructions, but without other
compile-time and runtime benefits that make language-based
constructions far better choices when they are applicable. Usages
should be restricted to the control of strictly internal
implementation matters inside a class or package, and must either
avoid or document any consequent violations of ordering or safety
properties expected by users of a class employing them.
Reachability. Method reachabilityFence
establishes an ordering for strong reachability (as defined in the
java.lang.ref
package specification) with respect to
garbage collection. Method reachabilityFence
differs from
the others in that it controls relations that are otherwise only
implicit in a program -- the reachability conditions triggering
garbage collection. As illustrated in the sample usages below,
this method is applicable only when reclamation may have visible
effects, which is possible for objects with finalizers (see Section
12.6 of the Java Language Specification) that are implemented in
ways that rely on ordering control for correctness.
Sample Usages
Safe publication. With care, method orderWrites
may be used to obtain the memory safety effects of final
for a field that cannot be declared as final
, because its
primary initialization cannot be performed in a constructor, in
turn because it is used in a framework requiring that all classes
have a no-argument constructor; as in:
class WidgetHolder {
private Widget widget;
public WidgetHolder() {}
public static WidgetHolder newWidgetHolder(Params params) {
WidgetHolder h = new WidgetHolder();
h.widget = new Widget(params);
return Fences.orderWrites(h);
}
}
Here, the invocation of orderWrites
ensures that the
effects of the widget assignment are ordered before those of any
(unknown) subsequent stores of h
in other variables that
make h
available for use by other objects. Initialization
sequences using orderWrites
require more care than those
involving final
fields. When final
is not used,
compilers cannot help you to ensure that the field is set correctly
across all usages. You must fully initialize objects
before the orderWrites
invocation that makes
references to them safe to assign to accessible variables. Further,
initialization sequences must not internally "leak" the reference
by using it as an argument to a callback method or adding it to a
static data structure. If less constrained usages were required,
it may be possible to cope using more extensive sets of fences, or
as a normally better choice, using synchronization (locking).
Conversely, if it were possible to do so, the best option would be
to rewrite class WidgetHolder
to use final
.
An alternative approach is to place similar mechanics in the (sole) method that makes such objects available for use by others. Here is a stripped-down example illustrating the essentials. In practice, among other changes, you would use access methods instead of a public field.
class AnotherWidgetHolder {
public Widget widget;
void publish(Widget w) {
this.widget = Fences.orderWrites(w);
}
// ...
}
In this case, the orderWrites
invocation occurs before the
store making the object available. Correctness again relies on
ensuring that there are no leaks prior to invoking this method, and
that it really is the only means of accessing the
published object. This approach is not often applicable --
normally you would publish objects using a thread-safe collection
that itself guarantees the expected ordering relations. However, it
may come into play in the construction of such classes themselves.
Safely updating fields. Outside of the initialization
idioms illustrated above, Fence methods ordering writes must be
paired with those ordering reads. To illustrate, suppose class
c
contains an accessible variable data
that should
have been declared as volatile
but wasn't:
class C {
Object data; // need volatile access but not volatile
// ...
}
class App {
Object getData(C c) {
return Fences.orderReads(c).data;
}
void setData(C c) {
Object newValue = ...;
c.data = Fences.orderWrites(newValue);
Fences.orderAccesses(c);
}
// ...
}
Method getData
provides an emulation of volatile
reads of (non-long/double) fields by ensuring that the read of
c
obtained as an argument is ordered before subsequent
reads using this reference, and then performs the read of its
field. Method setData
provides an emulation of volatile
writes, ensuring that all other relevant writes have completed,
then performing the assignment, and then ensuring that the write is
ordered before any other access. These techniques may apply even
when fields are not directly accessible, in which case calls to
fence methods would surround calls to methods such as c.getData()
. However, these techniques cannot be applied to
long
or double
fields because reads and writes of
fields of these types are not guaranteed to be
atomic. Additionally, correctness may require that all accesses of
such data use these kinds of wrapper methods, which you would need
to manually ensure.
More generally, Fence methods can be used in this way to achieve
the safety properties of volatile
. However their use does
not necessarily guarantee the full sequential consistency
properties specified in the Java Language Specification chapter 17
for programs using volatile
. In particular, emulation using
Fence methods is not guaranteed to maintain the property that
volatile
operations performed by different threads are
observed in the same order by all observer threads.
Acquire/Release management of threadsafe objects. It may be possible to use weaker conventions for volatile-like variables when they are used to keep track of objects that fully manage their own thread-safety and synchronization. Here, an acquiring read operation remains the same as a volatile-read, but a releasing write differs by virtue of not itself ensuring an ordering of its write with subsequent reads, because the required effects are already ensured by the referenced objects. For example:
class Item {
synchronized f(); // ALL methods are synchronized
// ...
}
class ItemHolder {
private Item item;
Item acquireItem() {
return Fences.orderReads(item);
}
void releaseItem(Item x) {
item = Fences.orderWrites(x);
}
// ...
}
Because this construction avoids use of orderAccesses
,
which is typically more costly than the other fence methods, it may
result in better performance than using volatile
or its
emulation. However, as is the case with most applications of fence
methods, correctness relies on the usage context -- here, the
thread safety of Item
, as well as the lack of need for full
volatile semantics inside this class itself. However, the second
concern means that it can be difficult to extend the ItemHolder
class in this example to be more useful.
Avoiding premature finalization. Finalization may occur
whenever a Java Virtual Machine detects that no reference to an
object will ever be stored in the heap: A garbage collector may
reclaim an object even if the fields of that object are still in
use, so long as the object has otherwise become unreachable. This
may have surprising and undesirable effects in cases such as the
following example in which the bookkeeping associated with a class
is managed through array indices. Here, method action
uses a reachabilityFence
to ensure that the Resource
object is not reclaimed before bookkeeping on an associated
ExternalResource has been performed; in particular here, to ensure
that the array slot holding the ExternalResource is not nulled out
in method Object.finalize()
, which may otherwise run
concurrently.
class Resource {
private static ExternalResource[] externalResourceArray = ...
int myIndex;
Resource(...) {
myIndex = ...
externalResourceArray[myIndex] = ...;
...
}
protected void finalize() {
externalResourceArray[myIndex] = null;
...
}
public void action() {
try {
// ...
int i = myIndex;
Resource.update(externalResourceArray[i]);
} finally {
Fences.reachabilityFence(this);
}
}
private static void update(ExternalResource ext) {
ext.status = ...;
}
}
Here, the call to reachabilityFence
is nonintuitively
placed after the call to update
, to ensure that
the array slot is not nulled out by Object.finalize()
before
the update, even if the call to action
was the last use of
this object. This might be the case if for example a usage in a
user program had the form new Resource().action();
which
retains no other reference to this Resource. While probably
overkill here, reachabilityFence
is placed in a finally
block to ensure that it is invoked across all paths in the
method. In a method with more complex control paths, you might
need further precautions to ensure that reachabilityFence
is encountered along all of them.
It is sometimes possible to better encapsulate use of
reachabilityFence
. Continuing the above example, if it
were OK for the call to method update to proceed even if the
finalizer had already executed (nulling out slot), then you could
localize use of reachabilityFence
:
public void action2() {
// ...
Resource.update(getExternalResource());
}
private ExternalResource getExternalResource() {
ExternalResource ext = externalResourceArray[myIndex];
Fences.reachabilityFence(this);
return ext;
}
Method reachabilityFence
is not required in
constructions that themselves ensure reachability. For example,
because objects that are locked cannot in general be reclaimed, it
would suffice if all accesses of the object, in all methods of
class Resource (including finalize
) were enclosed in synchronized (this)
blocks. (Further, such blocks must not include
infinite loops, or themselves be unreachable, which fall into the
corner case exceptions to the "in general" disclaimer.) However,
method reachabilityFence
remains a better option in cases
where this approach is not as efficient, desirable, or possible;
for example because it would encounter deadlock.
Formal Properties.
Using the terminology of The Java Language Specification chapter 17, the rules governing the semantics of the methods of this class are as follows:
The following is still under construction.
orderWrites(p)
or
orderAccesses(p)
orderReads(p)
or
orderAccesses(p)
orderWrites(p)
or
orderAccesses(p)
orderAccesses(p)
reachabilityFence(p)
p.finalize()
or of enqueing any Reference
constructed with argument p
Modifier and Type | Method and Description |
---|---|
static <T> T |
orderAccesses(T ref)
Informally: Ensures that accesses (reads or writes) using the
given reference prior to the invocation of this method occur
before subsequent accesses.
|
static <T> T |
orderReads(T ref)
Informally: Ensures that a read of the given reference prior to
the invocation of this method occurs before a subsequent use of
the given reference with the effect of reading or writing a
field (or if an array, element) of the referenced object.
|
static <T> T |
orderWrites(T ref)
Informally: Ensures that a use of the given reference with the
effect of reading or writing a field (or if an array, element)
of the referenced object, prior to the invocation of this
method occur before a subsequent write of the reference.
|
static void |
reachabilityFence(Object ref)
Ensures that the object referenced by the given reference
remains strongly reachable (as defined in the
java.lang.ref package documentation), regardless of any prior
actions of the program that might otherwise cause the object to
become unreachable; thus, the referenced object is not
reclaimable by garbage collection at least until after the
invocation of this method. |
public static <T> T orderReads(T ref)
orderWrites(T)
and/or orderAccesses(T)
for the given reference. For details, see the
class documentation for this class.ref
- the reference. If null, this method has no effect.public static <T> T orderWrites(T ref)
ref
- the reference. If null, this method has no effect.public static <T> T orderAccesses(T ref)
ref
- the reference. If null, this method has no effect.public static void reachabilityFence(Object ref)
java.lang.ref
package documentation), regardless of any prior
actions of the program that might otherwise cause the object to
become unreachable; thus, the referenced object is not
reclaimable by garbage collection at least until after the
invocation of this method. Invocation of this method does not
itself initiate garbage collection or finalization.
See the class-level documentation for further explanation and usage examples.
ref
- the reference. If null, this method has no effect.