3

I have a data structure that I occasionally wish to modify, and occasionally wish to replace outright. At the moment, I'm storing this in an AtomicReference, and using synchrnonized blocks (synchronized on the AtomicReference itself, not its stored value) when I need to modify it, rather than replace it.

So something like:

public void foo(AtomicReference reference){
    synchronized(reference){
        reference.get()
            .performSomeModification();
    }
}

Notice that the modifying call is a member of the wrapped value, not the atomic reference, and is not guaranteed to have any thread safety of its own.

Is this safe? Findbugs (a freeware code reviewing tool) had this to say about it, so now I'm worried there's something happening under the hood, where it may be prematurely releasing the lock or something. I've also seen documentation referencing AtomicReference as specifically for immutable things.

Is this safe? If it isn't I could create my own Reference-storing class that I would be more certain about the behavior of, but I don't want to jump to conclusions.

Edward Peters
  • 3,623
  • 2
  • 16
  • 39
  • Findbugs is only commenting that the synchronization may be redundant, or not do what you want. – user207421 Sep 09 '16 at 00:11
  • @EJP It does say that it will not prevent modification... though on a second read, I think that means that one `synchronized` block could interleave with `AtomicReference.get()` and `.set()`, not that it will break the actual `synchronized` behavior – Edward Peters Sep 09 '16 at 00:18
  • It will prevent modification if everybody synchronizes, which is the only correct way to use synchronization anyway, so it's rather a pointless statement. There's nothing there about breaking the `synchronized` behaviour. – user207421 Sep 09 '16 at 00:41
  • Why are you using an AtomicReference to begin with? – shmosel Sep 09 '16 at 01:02
  • @shmosel I was considering just writing a generic reference to allow two classes to potentially replace (rather than modify) a reference to a data structure in a shared way. Several people (both on SO and in the office) suggested I use AtomicReference instead, but I'm starting to have second thoughts. – Edward Peters Sep 09 '16 at 02:23
  • Why do you need a reference object altogether? Nothing in the code you've shown can't be replaced with a simple field. – shmosel Sep 09 '16 at 02:33
  • @shmosel I only showed the case where I modify the object, as that's where I'm unsure of the behavior - but as mentioned, there are also cases where I want to replace it outright, like `reference.set(newValue)` – Edward Peters Sep 09 '16 at 02:39
  • What's wrong with `value = newValue`? – shmosel Sep 09 '16 at 02:40
  • @shmosel That won't update the reference elsewhere. – Edward Peters Sep 09 '16 at 16:21
  • You mean you're passing it as an argument. – shmosel Sep 09 '16 at 16:23
  • @shmosel I'm not sure exactly what you're saying, but possibly. I have (at least) two classes that need to look at the same data structure. If I allow them to reference it directly, then if one class over-writes the reference it will have no effect on the other, and thy will no longer be looking at the same data. By wrapping the data structure in a reference class (Atomic or othewise), the "inner" reference can be over-written, and both classes will "see" the change. So, I initially pass the same AtomicReference as an argument to each class, to ensure they're looking at the same place. – Edward Peters Sep 09 '16 at 16:37

2 Answers2

4

From the linked documentation:

For example, synchronizing on an AtomicBoolean will not prevent other threads from modifying the AtomicBoolean.

It can't prevent other threads from modifying the AtomicBoolean because it can't force other threads to synchronize on the AtomicBoolean.

If I understand your question correctly, your intention is to synchronize calls to performSomeModification(). The code you've written will achieve that, if and only if every call to performSomeModification() is synchronized on the same object. As in the example from the docs, the basic problem is the enforceability of that requirement. You can't force other callers to synchronize on the AtomicReference. You or some other developer who comes after you could easily call performSomeModification() without external synchronization.

You should make it hard to use your API incorrectly. Since AtomicReference is a generic type (AtomicReference<V>), you can enforce the synchronization in a variety of ways, depending on what V is:

  • If V is an interface, you could easily wrap the instance in a synchronized wrapper.
  • If V is a class that you can modify, you could synchronize performSomeModification(), or create a subclass in which it is synchronized. (Possibly an anonymous subclass produced by a factory method.)
  • If V is a class that you cannot modify, it may be difficult to wrap. In that case, you could encapsulate the AtomicReference in a class that you do control, and have that class perform the required synchronization.
Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82
  • +1: _You can't force other callers to synchronize on_ [a publicly visible object...] _You should make it hard to use your API incorrectly._ – Solomon Slow Sep 09 '16 at 13:06
  • @EdwardPeters, In other words, move the `synchronized` statement into the `performSomeModification()` method itself. – Solomon Slow Sep 09 '16 at 13:11
2

Are Mutable Atomic References a Bad Idea?

Definitely not! AtomicReference is designed to provide thread-safe, atomic updates of the underlying reference. In fact, the Javadoc description of AtomicReference is:

An object reference that may be updated atomically.

So they most definitely are designed to be mutated!

Is this safe?

It depends on what you mean by "safe", and what the rest of your code is doing. There's nothing inherently unsafe about your snippet of code in isolation. It's perfectly valid, though perhaps a bit unusual, to synchronize on an AtomicReference. As a developer unfamiliar with this code, I would see the synchronization on reference and assume that it means that the underlying object may be replaced at any time, and you want to make sure your code is always operating on the "newest" reference.

The standard best practices for synchronization apply, and violating them could result in unsafe behavior. For example, since you say performSomeModification() is not thread-safe, it would be unsafe if you accessed the underlying object somewhere else without synchronizing on reference.

public void bar(AtomicReference reference) {
    // no synchronization: performSomeModification could be called on the object 
    // at the same time another thread is executing foo()
    reference.get().performSomeModification();

}

If could also be "unsafe" if your application requires that only one instance of the underlying object be operated on at any one time, and you haven't synchronized on the reference when .set()ing it:

public void makeNewFoo(AtomicReference reference) {
    // no synchronication on "reference", so it may be updated by another thread 
    // while foo() is executing performSomeModification() on the "old" reference
    SomeObject foo = new SomeObject();
    reference.set(foo);
}

If you need to synchronize on the AtomicReference, do so, it's perfectly safe. But I would highly recommend adding a few code comments about why you're doing it.

Jason Hoetger
  • 7,543
  • 2
  • 16
  • 18
  • I'm sure AtomicReference is good when the reference is mutable, but I'm not sure about the referenced object itself being mutable, if that makes sense? So `AtomicReference.set(newValue)` is a thing, but there isn't a method that just takes a lanbda to operate on the value and return nothing. – Edward Peters Sep 09 '16 at 02:22
  • @Jason: Good one. – hagrawal7777 Sep 09 '16 at 02:24
  • @EdwardPeters There's also nothing inherently wrong with having a mutable object in an AtomicReference. It's just not designed to solve that particular problem of thread-safe access on the underlying object. You could roll your own wrapper class that has a synchronized method that takes a lambda, and it could use an AtomicReference internally. – Jason Hoetger Sep 09 '16 at 04:27