InfoQ

InfoQ

Article

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

Concurrency and Immutability

Posted by Dhanji Prasanna on Jun 26, 2009

Sections
Development,
Architecture & Design
Topics
Programming ,
Java ,
Object Oriented Design ,
Design
Tags
Concurrency

Concurrency is increasingly a very important aspect of modern applications. As we scale to higher levels of traffic and demand, there is a greater need for multiple concurrent threads of execution. As such, the role of objects managed by the dependency injector is extremely important. Singletons are a particularly significant example of this need.

In a large web application handling several hundreds of a requests per minute, a poorly designed singleton can be a serious bottleneck. It introduces ceilings on concurrent performance, and can even render the application unscalable under certain conditions.

Poorly concurrent behavior is also more common than you might think. And since its effects are only highlighted during performance testing, it can be difficult to identify and mitigate. So it is quite relevant for us to study the effects of concurrency on singletons.

Mutability is an essential variable in this problem. Some pitfalls are contained within the idea of immutability, too, so let's explore exactly what it means to be immutable. To change things up, we'll look at this as a series of puzzles.

Immutablity Puzzle #1

Is the following class, Book, immutable?

public class Book {
	private String title;
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
}

Answer #1

This one's easy: no. The value of its title field can be changed arbitrarily by calling setTitle() so it is clearly not immutable. We can make Book immutable by declaring title final as shown below:

public class ImmutableBook {
	private final String title;
	public ImmutableBook(String title) {
		this.title = title;
	}
	public String getTitle() {
		return title;
	}
}

Once set in the constructor, the value of title cannot change.

Immutablity Puzzle #2

Is the following class, AddressBook, immutable?

public class AddressBook {
	private final String[] names;
	public AddressBook(String[] names) {
		this.names = names;
	}
	public String[] getNames() {
		return names;
	}
}

Answer #2

The value of the names field is set once in the constructor and is declared final. So AddressBook should be immutable, right? No! In fact, the subtle point is that since names is an array, only the reference to it is immutable by declaring it final. The following code is perfectly legal and can potentially lead to a world of hurt where multiple threads are concerned:

public class AddressBookMutator {
	private final AddressBook book;

	@Inject
	public AddressBookMutator(AddressBook book) {
		this.book = book;
	}

	public void mutate() {
		String[] names = book.getNames();


		for (int i = 0; i < names.length; i++)
			names[i] = "Censored!"

		for (int i = 0; i < names.length; i++)
			System.out.println(book.getNames()[i]);
}
} 

Method mutate() destructively updates the array, even though field names is unchangeable. If you run the program it prints out "Censored!" for every name in the book. The only real solution to this problem is not to use arrays, or to use them very sparingly behind well understood safeguards and documentation. Prefer library collections (such as those found in java.util) classes where possible as these can be guarded by unmodifiable wrappers. See puzzle #3 for an illustration of using java.util.List instead of an array.

Immutablity Puzzle #3

Is the following class, BetterAddressBook, immutable?

 public class BetterAddressBook {
	private final List<String> names;

 	public BetterAddressBook(List<String> names) {
		this.names = Collections.unmodifiableList(names);
	 }
 	public List<String> getNames() {
		return names;
 }
}

Answer #3

Thankfully, yes, BetterAddressBook is immutable. The wrapper provided by the Collections library ensures that no updates can be made to the list once it has been set. The following code, though it compiles, results in an exception at runtime:

BetterAddressBook book = new BetterAddressBook(Arrays.asList("Landau", "Weinberg", "Hawking"));
book.getNames().add(0, "Montana");

Immutablity Puzzle #4

This is a variant on puzzle #3. Take the same BetterAddressBook class we saw earlier. Is it at all possible to construct it in such a way that I can mutate it? You are not allowed to change the code of BetterAddressBook.

The answer is quite simple, if a bit confounding:

List<String> physicists = new ArrayList<String>();
physicists.addAll(Arrays.asList("Landau", "Weinberg", "Hawking"));
BetterAddressBook book = new BetterAddressBook(physicists);
physicists.add("Einstein");

Now an iteration through BetterAddressBook's list of names:

for (String name : book.getNames())
System.out.println(name);

So, really, we must revise what we said in the answer to puzzle #3. BetterAddressBook is only immutable if its dependency list is not leaked anywhere else. Better yet, we can rewrite a completely safe version of it by copying the list at the time of its construction:

@Immutable
public class BestAddressBook {
	private final List<String> names;
	public BestAddressBook(List<String> names) {
		this.names = Collections.unmodifiableList(new ArrayList<String>
(names));
	}
	public List<String> getNames() {
		return names;
	}
}

Now you are free to leak and mutate the original list:

List<String> physicists = new ArrayList<String>();
physicists.addAll(Arrays.asList("Landau", "Weinberg", "Hawking"));


BetterAddressBook book = new BetterAddressBook(physicists);


physicists.clear();
physicists.add("Darwin");
physicists.add("Wallace");
physicists.add("Dawkins");

for (String name : book.getNames())
	System.out.println(name);

...and BestAddressBook remains unaffected:

Landau
Weinberg
Hawking

While it may not always be necessary to take such a cautious approach, it is advisable to copy argument lists if you are at all unsure about them escaping into other uses subsequent to construction of the immutable object.

Immutablity Puzzle #5

Is the following class, Library, immutable? (Recall Book from puzzle #1)

public class Library {
	private final List<Book> books;

	public Library(List<Book> books) {
		this.books = Collections.unmodifiableList(new ArrayList<Book>(books));
	}
	public List<Book> getBooks() {
		return books;
	}
}

Answer #5

Library depends on a list of Books, but it takes care to wrap the incoming list in an unmodifiable wrapper and copies it prior to doing so. Of course, its only field is final too. Everything looks right--or does it? Actually, it turns out that Library is mutable! While the collection of books is unchangable, there is still the Book object itself. Which, as you may recall from the first puzzle, allows its title to be set:

Book book = new Book();
book.setTitle("Dependency Injection")
Library library = new Library(Arrays.asList(book));
library.getBooks().get(0).setTitle("The Tempest"); //mutates Library

The golden rule with immutability and object graphs is that every dependency of an object must also be immutable. In the case of BestAddressBook, we got lucky, since Strings in Java are already immutable. Take care to ensure that every dependency you have is safely immutable before declaring an object as such. The @Immutable annotation seen in " Immutablity Puzzle #4" helps a great deal in conveying and documenting this intent.


This article is excerpted from the upcoming title Dependency Injection by Dhanji R. Prasanna and published by Manning Publications. It explores the concept of immutability in a series of five puzzles. For the table of contents, the Author Forum, and other resources, go to http://manning.com/prasanna/.

InfoQ readers will receive a 25% discount when ordering via manning.com. Use discount code, "infoq25".

15 comments

Watch Thread Reply

Not sure if the presentation is correct by Matej Tymes Posted
Re: Not sure if the presentation is correct by Matej Tymes Posted
Re: Not sure if the presentation is correct by Matej Tymes Posted
Re: Not sure if the presentation is correct by Matej Tymes Posted
Re: Not sure if the presentation is correct by Adel Manatad Posted
Re: Not sure if the presentation is correct by Adel Manatad Posted
Re: Not sure if the presentation is correct by Matej Tymes Posted
Re: Not sure if the presentation is correct by Jules Jacobs Posted
Re: Not sure if the presentation is correct by Mario Gleichmann Posted
Re: Not sure if the presentation is correct by Rameshkumar Ramasamy Posted
Re: Not sure if the presentation is correct by Frederick Polgardy Posted
Re: Not sure if the presentation is correct by Chung Juan Posted
Immutablity Puzzle #2 error by Mike S Posted
Puzzle 3 is also mutable! by Mario Gleichmann Posted
Re: Puzzle 3 is also mutable! by Arturs Licis Posted
  1. Back to top

    Not sure if the presentation is correct

    by Matej Tymes

    From my point of view non of this classes can't provide you immutablity guaranty if they can be subclassed.

    For example a method can return a MUTABLE subclass of this "IMMUTABLE" class and the whole concept of immutability is lost.

  2. Back to top

    Re: Not sure if the presentation is correct

    by Matej Tymes

    to prove this statement just think that I will create this class extension:



    public class MyBestAddressBook extends BestAddressBook {

        private List<String> names;



        public MyBestAddressBook(List<String> names) {

            super(names);

            this.names = new ArrayList<String>(names);

        }



        public List<String> getNames() {

            return names;

        }



        public void addName(String name) {

            names.add(name);

        }

    }



    And then test it:



    public class TestImmutabilityKiller {



        public static BestAddressBook getAddressBook(List<String> bookNames) {

            return new MyBestAddressBook(bookNames);

        }



        public static void main(String[] args) {

            BestAddressBook addressBook = getAddressBook(Arrays.asList("Men at Arms", "Guards! Guards!"));



            System.out.println("before modification:");

            System.out.println(addressBook.getNames());



            if(addressBook instanceof MyBestAddressBook) {

                ((MyBestAddressBook)addressBook).addName("Faust - Eric");

            }



            System.out.println("after modification:");

            System.out.println(addressBook.getNames());

        }

    }



    After you test it the result will be:

    before modification:

    [Men at Arms, Guards! Guards!]

    after modification:

    [Men at Arms, Guards! Guards!, Faust - Eric]



    As you can see I have been able to add a new book name into the "IMMUTABLE" BestAddressBook class

  3. Back to top

    Re: Not sure if the presentation is correct

    by Matej Tymes

    but otherwise, its quite nice article :)

  4. Back to top

    Re: Not sure if the presentation is correct

    by Matej Tymes

    to resolve this you must:



      a. mark the class BestAddressBook as final or

      b. mark the method BestAddressBook.getNames() as final or

      c. mark the constructor of class BestAddressBook as private and use static factory method or inner static builder class for new instance creation or

      d. maybe there is also some different solution


  5. Back to top

    Re: Not sure if the presentation is correct

    by Adel Manatad

    Matej's explanation of the effects of marking a class and methods as final is absolutely correct, but his "context" for making his argument (albeit just to play the devil's advocate) is just plain ugly and wrong design decision!

    Why would you want to repeat the already stable base class' List<String> member "names" in a subclass? Your intention is just too wicked, or you might just lack OOP design skills. As you can see, to create your argument you are forced to use the "instanceof" operator in an if statement, which usually shows as a sign of a smelly code.

    If you would really need to subclass BestAddressBook to implement an "is a" relationship, then you don't need to define "names" list again on the subclass. Making so will defeat the very essence of "structural inheritance".</string>

  6. Back to top

    Re: Not sure if the presentation is correct

    by Adel Manatad

    Your example was not able to change the BestAddressBooks' names field. What it changed was the subclass' names field. It just showed that BestAddressBook is really immutable indeed. :)

  7. Back to top

    Re: Not sure if the presentation is correct

    by Matej Tymes

    Yes, but, when you for example create a service that is doing something with the BestAddressBook you expect that the BestAdressBook is imutable and everything is fine. Fine till somebody in the future (and it is not you) decides that for his subproject he would like to extend the class with sorting functionality. He can't modify your BestAddressBook and he can't modify your cool service. So he decides that the easiest thing is to extend the BestAddressBook (in the way I have described) and as he assumes everything will work just fine. But it won't be as you have allowed him to destroy it (because you were expecting that everybody will create an execelent code). Of course you could check if the BestAddressBook you have received isn't a subclass, but from my point of view this is really not the best aproach.

  8. Back to top

    Re: Not sure if the presentation is correct

    by Jules Jacobs

    The answer to the second immutability puzzle is wrong. You're defining getNames(int n) but calling getNames(). The second class *is* immutable.

  9. Back to top

    Immutablity Puzzle #2 error

    by Mike S

    In "Immutablity Puzzle #2" class AddressBook does not expose names array reference via getter so it is actually "immutable" from this article's point of view.

  10. Back to top

    Re: Not sure if the presentation is correct

    by Mario Gleichmann

    Although AdressBook does in fact not return the whole names array, the class is still mutable, in that the creator of an instance of an AdressBook still holds a direct reference to the names array and therefore could alter it afterwards:

    String[] friends = new String[]{ "Helen", "John", "Karen", "Peter" };

    AdressBook adressBook = new AdressBook( friends );

    fiends[1] = "Jonas";

  11. Back to top

    Puzzle 3 is also mutable!

    by Mario Gleichmann

    BetterAdressBook is also mutable since the creator of an instance may also keeps holding a direct reference to the List which he passed to the constructor:


    List<String> friends = new ArrayList<String>();

    friends.add( "Helen" );

    ...



    BetterAdressBook adressBook = new BetterAdressBook( friends );



    friends.remove( 1 );

    friends.add( 1, "Jonas" );</string></string>

  12. Back to top

    Re: Puzzle 3 is also mutable!

    by Arturs Licis

    Actually this is reviewed in Puzzle #4:


    So, really, we must revise what we said in the answer to puzzle #3. BetterAddressBook is only immutable if its dependency list is not leaked anywhere else.

  13. Back to top

    Re: Not sure if the presentation is correct

    by Rameshkumar Ramasamy

    I am not able to Compile #Puzzle4, using Eclipse 3.3 + JDK 1.6.0_12. Error is "Immutable cannot be resolved to a type". May I know which jar file I'm missing, that need to be included in the classpath, to get it compiled.

  14. Back to top

    Re: Not sure if the presentation is correct

    by Frederick Polgardy

    I believe it was intended to be a marker annotation that you could create on your own to denote immutability in your system. @Immutable isn't a Java API.

  15. Back to top

    Re: Not sure if the presentation is correct

    by Chung Juan

    It is true that you modifiy names, but this is the field in your class MyBestAddressBook, it is your duty to control whether this filed is immutable. if you try to modify the field names in the super class, an exception may be thrown!

Educational Content

Attila Szegedi on JVM and GC Performance Tuning at Twitter

Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.

10 tips on how to prevent business value risk

One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.

Interview: Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives

InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.

Beauty Is in the Eye of the Beholder

Alex Papadimoulis discusses ugly code, where it comes from, how to avoid it, and how to get rid of it.

Architecting Visa for Massive Scale and Continuous Innovation

John Davies examines Visa’s architecture and shows how enterprises have architected complex integrations incorporating Hadoop, memcached, Ruby on Rails, and others to deliver innovative solutions.

Max Protect: Scalability and Caching at ESPN.com

Sean Comerford unveils ESPN.com’s architecture, what components are used and why, and the current changes the website goes through.

The Seven Deadly Sins of Enterprise Agile Adoption

Are there repeated patterns of failure on Enterprise Agile Enablement efforts? Sanjiv and Arlen discuss Seven Deadly Sins to avoid when adopting Agile in an enterprise.

Questions for an Enterprise Architect

Erik Dörnenburg answers: What is Enterprise and Evolutionary Architecture?, discussing 4 issues: Turning strategy into execution, Ensuring conformance, Where do the architects sit? Buying or building?