3

I have seen examples where even though the object is immutable there are situations in which we need to update some fields and as I understand it, the object is passed to a constructor which makes a copy with updated values but the original is not changed. Fine, but what if two threads want to do this? Do we not run into synchronization problems if they both are changing the same field? And how if now there are new objects made (rather than a changed original) do multiple threads know that the values of fields have changed?

I have seen many examples of how to make an immutable class but it is not clear to me how updating attributes works in a multithreaded environment, how synchronization is not required and as I mentioned above, how to determine what object now represents the current state.

Here is an article in which changing an immutable object is discussed: https://jlordiales.me/2012/12/24/the-ins-and-outs-of-immutability/

16
  • 3
    Immutable means it doesn't change, period. You don't update fields in an immutable objects because this capability isn't possible. If you create a copy, then each thread will create its own copy, so there is no synchronization issue. Do you have any example of a scenario you do not understand? Jun 15, 2018 at 18:47
  • 5
    For accounts, instead of having some value that is changed, you'd instead model it as a series of immutable transactions, chained one right after another. And to get the current value (at any given point in time), you'd just add them all up.
    – Telastyn
    Jun 15, 2018 at 19:20
  • 2
    @Jeff: Google "temporal database". It's a thing. Records are immutable; they're never updated or deleted. You want to change an address, you create a new record with the updated address, the change time/date, and your user ID/key/etc. No record changes or disappears, the database becomes basically a bunch of snapshots of its last known state, and you can view the state of the account at any point in its entire history. Or you can track down when a change was made -- and if you stored it, also who made the change.
    – cHao
    Jun 15, 2018 at 20:28
  • 1
    @Jeff: You can always transform any "mutable" value into an immutable value by adding a version field. I recommend reading some of Rich Hickey's articles and watching some of his presentations about Clojure; he explains very well the dangers of conflating identity, value, and state, and how Clojure fixes this. Jun 16, 2018 at 6:49
  • 1
    "Can an account object which contains a balance be immutable, so that when the balance changes" – That is not how banking systems are designed. And in fact, it is not how banking systems in the real world work. Both in software and in the real world, you create an immutable transaction slip (in the real world, this used to be literally an actual slip of paper that was shipped by horse and carriage to the central office) and the balance is then simply the sum of the transactions. So, the transaction is data and the balance is a function, which is exactly the dual of what gets usually taught Jun 16, 2018 at 10:32

2 Answers 2

9

Fine, but what if two threads want to do this?

Then one thread makes one change (and gets one version of the object) and another thread makes another change (and gets a different, independent version of the object).

Do we not run into synchronization problems if they both are changing the same field?

Nope, because each thread gets their own copy of the object.

And how if now there are new objects made (rather than a changed original) do multiple threads know that the values of fields have changed?

They don't. That's the point.

how to determine what object now represents the current state.

And that's the gotcha. Immutability works great when things are values, or when things really are independent. But if you have an object that represents a single record - say from a database - then changes need to be merged back into that single record. That can be last in wins. That can be some manner of versioning/timestamping. There's lots of options with various tradeoffs.

But the key bit is that synchronization only needs to happen at that last step where the changes are merged and actually take effect on the single source of truth.

6
  • This is the first time in a discussion of immutability that I have read of merging changes -- can you provide an article which goes into this in some detail? In my experience, you have things like a customer account which changes or it is not useful. It sounds like you could have two separate CS reps modifying the account and then there would be two copies plus the original that would have to be merged at some point, right?
    – Jeff
    Jun 15, 2018 at 19:11
  • @Jeff - something like en.wikipedia.org/wiki/Optimistic_concurrency_control is an example of one way to take different changes an integrate them back into the source of truth.
    – Telastyn
    Jun 15, 2018 at 19:14
  • @Jeff the data model in your application is not necessarily the source of truth for this data. You would typically still have a single authoritative database that enforces a logically consistent data model (ACID transactions). These approaches work very well if your application handles short-lived requests rather than long-lived sessions and tears down the data model after each request. This has become the standard architecture for web applications.
    – amon
    Jun 15, 2018 at 19:16
  • @Telastyn: so it sounds like persistence/database representation is central to this. I am familiar with timestamps when updating a db record but discussions of immutability don't seem to (in the ones I've read) go into dbs or timestamps. Rather, they mention how to make a class immutable and how in fact when you change an object you are really making a copy but the final step I have yet to see mentioned until your very helpful answer.
    – Jeff
    Jun 15, 2018 at 19:25
  • @Telastyn: loved you "single source of truth" :D Keeping it to reference in the future. Jun 15, 2018 at 20:43
3

The article you linked talks about using the Builder Pattern to create an object. There's no conceptual difference between doing that and using a constructor. You simply treat all of the steps in the Builder as a single operation.

During the build process, you're not mutating an immutable object, because the object is not considered completely built until you call Build() on the builder, at which point you then have a fully constructed, immutable object.

This is also true of constructors. Many constructors have several steps within the body of the constructor method. All of these steps must be completed before the object is considered fully constructed and a usable immutable object results.


If you want to make a new immutable object from an existing immutable object with some of the values changed, what you now need is a new constructor or builder that takes an existing object as a parameter, and then set all of the attributes in the constructor or builder that you want to change.

For example:

public BookBuilder(Book book) {
    this.isbn = book.getIsbn();
    this.publicationYear = book.getPublicationYear();
    this.reviews = book.getReviews();
}

And then

Book originalBook = getRandomBook();

Book modifiedBook = new 
BookBuilder(originalBook).isbn("123456").publicationYear(2011).build();

The builder creates a copy of the original book, calls the methods in your fluent interfaces to set the desired fields to new values, and then the build() method freezes the new object in some way so that it becomes immutable.

Note that the author makes two concessions:

  1. The builder is not thread safe, and
  2. You still have to make copies of objects.

When you use constructors, you can set and change final or readonly members until the constructor finishes executing, at which point these members become unchangeable.

5
  • My understanding is that he creates a copy of the original object with different attributes. Are you disagreeing with the article or my interpretation?
    – Jeff
    Jun 15, 2018 at 23:38
  • No, that's how immutable objects work. That's how they work whether you use Builder or a constructor. He is not making the assertion that you are relieved from the overhead of making a copy; you still have to do that, even if you use a Builder. Jun 16, 2018 at 3:19
  • But isn't the omission of a discussion of merging, etc. confusing? Obviously I found it so.
    – Jeff
    Jun 16, 2018 at 7:26
  • What "merging" would you be referring to? See the update to my answer. Jun 16, 2018 at 16:32
  • The first answer mentions merging.
    – Jeff
    Jun 16, 2018 at 16:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.