JAVA Final KeyWord And Thread Safety

The Java final keyword

The Java final keyword is very loosely used to indicate that something "cannot change". Specifically— and perhaps confusingly from a language design point of view— it has a number of uses:

  • it is used to indicate that a class cannot be extended;
  • it is used to indicate that a method cannot be overridden;
  • it is used to indicate that a local variable cannot be changed once its value is set;
  • it is used to indicate that a static variable cannot be changed once set, in effect implementing "constants";
  • it is used to indicate that a value of an instance variable cannot be changed once set; this (due to an amendment to the JVM specification as of Java 5) makes accesses to that variable thread safe.

A common misconception about the Java final keyword is that it is essentially a performance optimisation. In Java, final is much more about program designthan performance, as we illustrate later when we look at some timings relating to the performance of final as a class modifier.

Using final to indicate that a class or method cannot be overridden

You can use final to specify that a whole class may not be extended, or similarly that a method cannot be overridden.

So when would you use final in this way? A common case is where:

  • a particular method that you are implementing needs to be public (e.g. because it extends some base class that requires it to be public, or simply because you expect callers from outside the package to call it)...
  • ...but you want classes that extend your class not to override your public method, because it contains some logic that you don't want replacing.

As an example, imagine if we were implementing a template for a swing component which would have a number of subclasses, but whose subclasses would always need to draw content on the "left" and "right" side of the component, at specific coordinates. We would probably therefore implement our template component as a subclass of JComponent, requiring a public paintComponent() method:

public abstract class LeftAndRightComponent extends JComponent {

  ...



  public void paintComponent(Graphics g) {

    // calculate x and y for left side

    ...

    paintLeftSide(x, y);

    // calculate x and y for right side

    ...

    paintRightSide(x, y);

  }



  protected abstract void paintLeftSide(int x, int y);



  protected abstract void paintRightSide(int x, int y);



}

 

But now we have the problem that overriding classes, as well as implementing paintLeftSide() and paintRightSide(), could "accidentally" override paintComponent()itself, in effect removing the whole functionality of the class! The solution is to declare this method final:

public abstract class LeftAndRightComponent extends JComponent {

  ...

  public final void paintComponent(Graphics g) {

    ...

    paintLeftSide(x, y);

    ...

    paintRightSide(x, y);

  }

  ...

}

 

Now, a subclass can still override paintLeftSide() and paintRightSide(), but not paintComponent, which is declared final.

Final classes

Cases for making an entire class final are less common, but would essentially be where allowing a subclass with differing implementation could lad to unexpected behaviour. A notable example from the JDK itself is the String class. A central design decision of Java strings is that they are immutable, and some of the inner workings of strings are tightly coupled with internals of the JVM itself. These factors together mean that allowing subclasses of String could lead to some unexpected behaviour, and so the String class is defined as being final:

public final class String {

  ...

}

Thread-safety with the Java final keyword

Essentially, final can be used to make sure that when you construct an object, another thread accessing that object doesn't see that object in a partially-constructed state, as could otherwise happen. This is because when used as an attribute on the variables of an object, final has the following important characteristic as part of its definition:

When the  constructor exits, the values of  final fields are  guaranteed to be visible to other threads accessing the constructed object.

Why is this necessary?

The final field is a means of what is sometimes called safe publication. Here, "publication" of an object means creating it in one thread and then having that newly-created object be referred to by another thread at some point in the future. When the JVM executes the constructor of your object, it must store values into the various fields of the object, and store a pointer to the object data. As in any other case of data writes, these accesses can potentially occur out of order, and their application to main memory can be delayed and other processors can be delayed unless you take special steps to combat this. In particular, the pointer to the object data could be stored to main memory and accessed before the fields themselves have been committed (this can happen partly because of compiler ordering: if you think about how you'd write things in a low-level language such as C or assembler, it's quite natural to store a pointer to a block of memory, and then advance the pointer as you're writing data to that block). And this in turn could lead to another thread seeing the object in an invalid or partially constructed state.

final prevents this from happening: if a field is final, it is part of the JVM specification that it must effectively ensure that, once the object pointer is available to other threads, so are the correct values of that object's final fields.(in a short. for thread safety .make sure your final field is initialized in constructor. because of the compiler re-order.)

Restrictions and limitations of using final

When you declare a field final, you must set the value once by the time the constructor exits. This means that you can declare a final field as follows:

public class MyClass {

  private final int myField = 3;(Gu)

  public MyClass() {

    ...

  }

}

 

or you can write the following:

public class MyClass {

  private final int myField;

  public MyClass() {

    ...

    myField = 3;

    ...

  }

}

 

class FinalFieldExample {

final int x;

int y;

static FinalFieldExample f;

public FinalFieldExample() {

x = 3;

y = 4;

}

static void writer() {

f = new FinalFieldExample();

}

static void reader() {

if (f != null) {

int i = f.x; // guaranteed to see 3

int j = f.y; // could see 0

}

}

}

 

It's important to emphasise that storing a reference to an object in a final field only makes the reference immutable, not the actual object. For examlple, if a list is declared as follows:

private final List myList = new ArrayList();

 

there's nothing to stop modifications to the list:

myList.add("Hello");

 

However, the following would not be possible:

myList = new ArrayList();

myList = someOtherList;

 

When should I use final?

One answer to this is "whenever you possibly can". Any field that you never expect to be changed (be that a primitive value, or a reference to an object, whether or not that particular object is itself immutable or not), should generally be declared final. Another way of looking at things is:

If your object is accessed by multiple threads, and you  don't declare its fields final, then you must provide  thread-safety by some other means.

Other means could include declaring the field volatile, using synchronized or an explicit Lock around all accesses to that field.

A typical case that people overlook is where an object is created by one thread and then later consumed by another thread, e.g. an object via a ThreadPoolExecutor. In this case, the object must still be made properly thread-safe: it doesn't matter that the accesses by different threads aren't concurrent. What matters is that the object is accessed by different threads at any point in its lifetime.

 

你可能感兴趣的:(thread)