The last few posts have highlighted some of the issues we need to be aware of when working with floating point numbers. In the next few posts we’re going to be looking at some concurrency related topics. This will include ThreadLocal
variables and Java 19 virtual threads.
One of these concurrency related topics is escaping object references. This can become an issue in applications with concurrent running threads.
Object Publication and Escaping
We publish an object whenever we make it available to some code outside its current scope. This happens when we pass an object to a method, return it from a non-private method, or store it into a publicly accessible field. We only want to pass objects that are in predictable, consistent states. This only happens after the object’s constructor returns.
An object escapes when it is published before it has been fully constructed. Escapes occur if we publish the object during object construction before it has been fully initialized. It is all too easy to incorrectly let the this
reference escape while the constructor is still being run. Other threads could then use that object in an inconsistent (partially initialised) state. This can cause difficult-to-debug threading issues and can compromise thread safety.
Escaping in Constructor Chains
Here’s an example to show the problem of incompletely initialized objects escaping. Even if the object escapes after initialization has completed, declaring a subclass can violate this.
public class SomeClass {
public SomeClass (AnotherClass obj) {
obj.foo(this);
}
}
public class NamedClass extends SomeClass {
private String name;
public NamedClass (AnotherClass obj, String name) {
super(obj);
}
public String getName() {
return name;
}
}
If the AnotherClass.foo(...)
method calls getName()
on the escaped object, it will get null
because at that point the constructor chain of NamedClass
has not finished.
Escaping with Threads
Starting a thread in a constructor is one of the most common ways of letting the this
reference escape. Here’s an example:
public class MyClass implements Runnable {
public MyClass() {
Thread thread = new Thread(this); // this explicitly escapes
thread.start();
}
@Override
public void run() {
System.out.println("Started...");
}
}
In this example, we have explicitly passed the this
reference to the Thread
constructor. The newly started thread might be able to see the enclosing this
object before it is fully constructed. With multiple concurrent threads, this could cause hard-to-find bugs.
There’s nothing inherently wrong with creating a Thread
inside a constructor. However, it’s not a good idea to start it immediately. If we do, we end up with an escaped this
reference, either explicitly (as above) or implicitly (see later).
Instead of starting the thread inside the constructor, we should start it after the constructor has finished. We can create a separate method to do that:
public class MyClass implements Runnable {
private final Thread thread;
public MyClass() {
thread = new Thread(this);
}
@Override
public void run() {
System.out.println("Started...");
}
public void start() {
thread.start();
}
}
We still publish the this
reference to the thread, but here we start the thread after the constructor ends. The object reference doesn’t escape to the thread before it’s fully constructed.
MyClass safeObject = new MyClass();
safeObject.start();
Escaping with Event Listeners
Using anonymous inner classes to create event listeners is another common way of letting the this
reference escape. Here’s an example:
public class MyClass {
private final int var; // implicitly initialised to zero
public MyClass(EventSource source) {
source.registerListener(
new EventListener() {
@Override
public void onEvent(Event e) {
doSomething(e);
}
});
// more initialization
var = 123;
}
// return value could either be 0 or 123
public int doSomething(Event e) {
return var;
}
}
Here we’ve created an anonymous inner EventListener
class. The this
reference escapes from the constructor, because inner classes have an implicit reference to their enclosing class.
An event could trigger the event listener after its creation, but before the MyClass
constructor has finished. The listener then calls the doSomething()
method of MyClass
. The return value could then either be 0 or 123. This would be a very subtle bug to hunt down.
This example is sneaky because the this
reference isn’t visible. It is implicitly escaping to the anonymous EventListener
class constructor. However, we’ll have the same problem if the reference is explicitly published too soon, as in the earlier threading example.
Signing Off
Lots more on concurrency to come!
Was this useful? Please share your comments on the blog post, and as always, stay safe and keep learning!