Java – Are immutable objects important only in multi-threaded applications and if so, how are shared immutable objects useful

functional programmingimmutabilityjavajavascript

I think the answer to the first part of my question is, "yes" — no point in making objects immutable in a single-threaded application (or I guess in a multi-threaded application if that particular object will not be shared among threads). If I am wrong, please explain why immutable objects are useful in a single-threaded app.

The second part is, if you are sharing an immutable object, what is it useful for if it can't be changed? Why is it even being shared? If it is "changed" so that a new and changed version of the object is created, say a shared counter, how is this shared object used if a change makes a new object — how does another thread access the new version? More broadly, how are immutable objects handled in a multi-threaded application — I guess how a shared but immutable counter would be incremented between multiple threads. I have searched extensively and I think all I read were discussions that involved some kind of locking or atomic operations which, I think, could be performed on a mutable object just as well.

And finally, if immutable shared counter is useful, are the machinations any simpler, is debugging any simpler, than just locking access to a mutable shared counter?

Best Answer

No, immutable objects are quite useful in general.

The first and most basic reason is that concurrency in a system doesn't require a multi-threaded application. Making say... a row in a database immutable provides a lot of benefit for change tracking, collision avoidance, syncing, and backups.

And while less valuable than in concurrent scenarios, immutable objects tend to be easier to use and debug because you know the state of the object across the lifetime of your app and you know that some function isn't misbehaving and mutating it on you. Also, any sort of exceptional state will show up immediately on object creation rather than during some mutation later on during processing. Those tend to be easier to identify, and happen in places where it is easier to recover or abort cleanly.

The second part is, if you are sharing an immutable object, what is it useful for if it can't be changed? Why is it even being shared?

The most obvious example is a configuration. You don't want to change it at runtime, but it's often needed by different parts of your code. Something like the current user. You don't want to change it, but will want to share it with different modules.

If it is "changed" so that a new and changed version of the object is created, say a shared counter, how is this shared object used if a change makes a new object -- how does another thread access the new version?

So the biggest thing with immutable objects (in most languages) is that writes of the object are atomic. Uninterruptable.

Say you want to change a few fields on a mutable object. The thread changes one, then another, then another. Any other thread can read the object in-between each of those steps. It'll see a half-changed object.

But if you want to change a few fields on an immutable object it's different. The thread makes a new object, changing the three fields it wants to change. Then it overwrites the shared reference in one, uninterruptable step. Any other thread can grab a reference to the object and know that it won't change. If it grabs the reference before the other thread does its write, it might get the old object but it can never get a half-changed object.

For a counter it doesn't much matter. Incrementing an int will be just as uninterruptable as assigning a reference to a new int (though that might not apply if you need counters bigger than an int, depending on your language, compiler, target CPU, etc.). Locks though are very costly in most languages/platforms so programmers will avoid them when it is safe to do so.

(For more info consider this question, which is adjacent to this one)