Thread-safety with the Java final keyword

As of Java 5, one particular use of the final keyword is a very important and often overlooked weapon in your concurrency armoury. 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.

Final object references

The fields on any object accessed via a final reference are also guaranteed to be at least as up to date as when the constructor exits. This means that:

Values of  final fields, including objects inside collections referred to by a  final reference, can be  safely read without synchronization.

Note that if you have a final reference to a collection, array, or some other mutable object, you must still synchronize all accesses to that object (or use a thread-safe collection such as a ConcurrentHashMap) if that collection is ever accessed by a thread other than the constructing thread.

Thus, immutable objects (ones where all fields are final and are either primitives or references to immutable objects) can be concurrently accessed without synchronization. It is also safe to read "effectively immutable" objects (ones whose fields aren't actually final, but in practice never change) via a final reference. However, from a program design perspective, you'd be wise to try and enforce immutability in this case (e.g. by wrapping a collection in aCollections.unmodifiableList()1 etc). That way, you'll spot bugs introduced when one of your colleagues naughtily attempts to modify a collection that you didn't intend to be modified!

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;
  public MyClass() {
    ...
  }
}

or you can write the following:

public class MyClass {
  private final int myField;
  public MyClass() {
    ...
    myField = 3;
    ...
  }
}

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值