Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 1922011-05-31 Category: Concurrency Java version: Java 5.0-7.0

GitHub Subscribe Free RSS Feed

Implicit Escape of "this"

by Dr. Heinz M. Kabutz
Abstract:
We should never allow references to our objects to escape before all the final fields have been set, otherwise we can cause race conditions. In this newsletter we explore how this is possible to do.

Welcome to the 192nd issue of The Java(tm) Specialists' Newsletter, which I am writing underneath some plane trees on the 1821 square of Chania. Last week Kirk Pepperdine ran his performance course at our conference room, so we had our hands full showing the students Cretan hospitality. I managed to convince them to join me on a short walk to a beach that no one I knew had ever been to. After about an hour of scrambling down a ravine, over boulders and squeezing through dense vegetation, we realised that the light was fading and decided to rather head back again. It was a great adventure for my three kids, aged 4 to 12. I am going back this Friday with brother-in-law Cam to scout the place out properly.

We are running an open spaces conference here in Crete at the end of August. Please let us know if you are interested in attending. We have space for about another five people. The conference is free, but of course you need to pay your own transport here.

Also, our conference venue is now available for hire. Crete is the perfect place for a European "bosberaad", a South African word meaning "bush summit". Companies often go "into the bush" (literally) to plan for the future and to train their employees. It's very relaxing to be away in nature and is a great enabler for learning.

NEW: We have revised our "Advanced Topics" course, covering Reflection, Java NIO, Data Structures, Memory Management and several other useful topics for Java experts to master. 2 days of extreme fun and learning. Extreme Java - Advanced Topics.

Implicit Escape of "this"

A few weeks ago, one of my friends sent me a question about section 3.2 of Java Concurrency in Practice. He could not understand how a method in Class A with a reference to an inner class in class B could obtain a reference to the outer class B. My friend thought that Brian might have meant that you could access the object via reflection.

For those of you who do not own the book yet, here is Brian's statement from Section 3.2 (this is one book that you really want to own though):

Java Concurrency in Practice, Section 3.2: A final [heinz: Brian means "final" as in "last", not as in Java final] mechanism by which an object or its internal state can be published is to publish an inner class instance, as shown in ThisEscape in Listing 3.7. When ThisEscape publishes the EventListener, it implicitly publishes the enclosing ThisEscape instance as well, because inner class instances contain a hidden reference to the enclosing instance.

//-------------------------Listing 3.7-------------------------
public class ThisEscape {
  public ThisEscape(EventSource source) {
    source.registerListener(
      new EventListener() {
        public void onEvent(Event e) {
          doSomething(e);
        }
      });
  }
}
  

Since my good friend was puzzling about this, I decided to expand the ThisEscape class with its own fields and doSomething() method and some additional code to demonstrate a possible data race.

In my class, I have a final field num that is initialized in the constructor. However, before it is set to 42, we register an anonymous inner class, which also leaks a pointer to the enclosing object.

import java.util.*;

public class ThisEscape {
  private final int num;

  public ThisEscape(EventSource source) {
    source.registerListener(
        new EventListener() {
          public void onEvent(Event e) {
            doSomething(e);
          }
        });
    num = 42;
  }

  private void doSomething(Event e) {
    if (num != 42) {
      System.out.println("Race condition detected at " +
          new Date());
    }
  }
}    
  

In my example, the Event and EventListener classes are kept as simple as possible:

public class Event { }
public interface EventListener {
  public void onEvent(Event e);
}
  

The EventSource is more complicated. In our case it is a Thread that repeatedly sends events to its latest listeners. Since we are trying to produce the race condition, we only ever send an event to a listener once. Thus the registerListener() method appends it to the end of the listeners queue and it is then taken off by the take() method call within the run() method of the thread.

import java.util.concurrent.*;

public class EventSource extends Thread {
  private final BlockingQueue<EventListener> listeners =
      new LinkedBlockingQueue<EventListener>();

  public void run() {
    while (true) {
      try {
        listeners.take().onEvent(null);
      } catch (InterruptedException e) {
        break;
      }
    }
  }

  public void registerListener(EventListener eventListener) {
    listeners.add(eventListener);
  }
}
  

All that is left is to construct a lot of ThisEscape objects in a row and watch the wheels come off:

public class ThisEscapeTest {
  public static void main(String[] args) {
    EventSource es = new EventSource();
    es.start();
    while(true) {
      new ThisEscape(es);
    }
  }
}
  

On my machine, I get race conditions immediately. What is interesting is that they stopped for a while and then started up again. However, I think this was probably a capacity problem with using a queue for the listeners. It would maybe be better to have a queue of capacity 1, in which case we could use an AtomicReference instead:

import java.util.concurrent.atomic.*;

public class EventSource2 extends Thread {
  private final AtomicReference<EventListener> listeners =
      new AtomicReference<EventListener>();

  public void run() {
    while (true) {
      EventListener listener = listeners.getAndSet(null);
      if (listener != null) {
        listener.onEvent(null);
      }
    }
  }

  public void registerListener(EventListener eventListener) {
    listeners.set(eventListener);
  }
}
  

Now we see the race conditions immediately and they do not stop when the queue gets too long.

Our lesson to learn is: We should never allow references to our objects to escape before all the final fields have been set, otherwise we can cause race conditions.

Heinz

P.S. One of our readers, Ulf, decided to log a bug against this behavior. He thinks that the compiler should detect and warn about such race conditions. Feel free to vote on this issue if you like.

Concurrency Articles Related Java Course

Extreme Java - Concurrency and Performance for Java 8
Extreme Java - Advanced Topics for Java 8
Design Patterns
In-House Courses

© 2010-2016 Heinz Kabutz - All Rights Reserved Sitemap
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.