Is Java ArrayList / String / atomic variable reading thread safe?

10,409

Solution 1

The only reason why it wouldn't be safe is if one thread were writing to a field while another thread was simultaneously reading from it. No race condition exists if the data is not changing. Making objects immutable is one way of guaranteeing that they are thread safe. Start by reading this article from IBM.

Solution 2

The members of an ArrayList aren't protected by any memory barriers, so there is no guarantee that changes to them are visible between threads. This applies even when the only "change" that is ever made to the list is its construction.

Any data that is shared between thread needs a "memory barrier" to ensure its visibility. There are several ways to accomplish this.

First, any member that is declared final and initialized in a constructor is visible to any thread after the constructor completes.

Changes to any member that is declared volatile are visible to all threads. In effect, the write is "flushed" from any cache to main memory, where it can be seen by any thread that accesses main memory.

Now it gets a bit trickier. Any writes made by a thread before that thread writes to a volatile variable are also flushed. Likewise, when a thread reads a volatile variable, its cache is cleared, and subsequent reads may repopulate it from main memory.

Finally, a synchronized block is like a volatile read and write, with the added quality of atomicity. When the monitor is acquired, the thread's read cache is cleared. When the monitor is released, all writes are flushed to main memory.

One way to make this work is to have the thread that is populating your shared data structure assign the result to a volatile variable (or an AtomicReference, or other suitable java.util.concurrent object). When other threads access that variable, not only are they guaranteed to get the most recent value for that variable, but also any changes made to the data structure by the thread before it assigned the value to the variable.

Solution 3

If the data is never modified after it's created, then you should be fine and reads will be thread safe.

To be on the safe side, you could make all of the data members "final" and make all of the accessing functions reentrant where possible; this ensures thread safety and can help keep your code thread safe if you change it in the future.

In general, making as many members "final" as possible helps reduce the introduction of bugs, so many people advocate this as a Java best practice.

Solution 4

Just as an addendum to everyone else's answers: if you're sure you need to synchronize your array lists, you can call Collections.synchronizedList(myList) which will return you a thread safe implementation.

Solution 5

I cannot see how reading from ArrayLists, Strings and primitive values using multiple threads should be any problem.

As long as you are only reading, no synchronization should be necessary. For Strings and primitives it is certainly safe as they are immutable. For ArrayLists it should be safe, but I do not have it on authority.

Share:
10,409
Jon M
Author by

Jon M

Updated on June 05, 2022

Comments

  • Jon M
    Jon M almost 2 years

    I've been mulling this over & reading but can find an absolute authoritative answer.

    I have several deep data structures made up of objects containing ArrayLists, Strings & primitive values. I can guarantee that the data in these structures will not change (no thread will ever make structural changes to lists, change references, change primitives).

    I'm wondering if reading data in these structures is thread safe; i.e. is it safe to recursively read variables from the objects, iterate the ArrayLists etc. to extract information from the structures in multiple threads without synchronization?

  • Admin
    Admin about 14 years
    Stings are internally imutable, but the variable can change out from under you, what you should be stating is that the variables that point to String and primatives should be marked 'final'.
  • Chandra Sekar
    Chandra Sekar about 14 years
    There are corner cases where this doesn't really guarantee thread safety. What is 2 threads each check the size of the list and then remove something?
  • Jon M
    Jon M about 14 years
    Excellent, thanks. This was certainly my instinct but wanted to check no-one knows of any cotcha's regarding iterators using non-thread safe variables etc. Perhaps I can stop obsessing & write some code now :)
  • Admin
    Admin about 14 years
    my answer talks about final 7 mins before :-)
  • james
    james about 14 years
    Yeah, pretty much all the java.util collections are thread-safe for read-only usage. The only gotch is LinkedHashMap/Set with a custom removeEldestEntry method.
  • brady
    brady about 14 years
    Unfortunately, this is wrong. In the absence of happens-before conditions (as defined by the language specification), words like "before", "while", and "after" have no meaning. Making the objects immutable using final is a good idea, so I won't downvote, but the idea that thread-safety can be obtained without a memory barrier is very, very wrong.
  • brady
    brady about 14 years
    @Chandru that sort of atomicity requires a synchronized block, but it is only guaranteed by the synchronizedList() wrapper; trying to do the same thing with an ArrayList directly would be unreliable.
  • brady
    brady about 14 years
    Like Amir Afghani, the idea that data can be shared across threads without a memory barrier is wrong and dangerous. But, also like Amir, you have a good idea with final.
  • Amir Afghani
    Amir Afghani about 14 years
    Can you provide an example based on what the OP described? If I'm wrong, the answer should be down voted.
  • Amir Afghani
    Amir Afghani about 14 years
    I was assuming that the values are set prior to reading them.
  • Amir Afghani
    Amir Afghani about 14 years
    But Erickson, I didn't say that its OK to share data across threads without a memory barrier without the qualification that the data is not changing. Please explain how this is dangerous.
  • james
    james about 14 years
    @erickson, to be fair, the question didn't really address that. he asked if multiple threads could read the data safely, which they can, provided the data has be published safely (what you are getting at). so maybe "very, very wrong" is a little strong and maybe you meant to say "yes as long is the data is published safely".
  • Amir Afghani
    Amir Afghani about 14 years
    "The members of an ArrayList aren't protected by any memory barriers, so there is no guarantee that changes to them are visible between threads" - The OP explicitly states that the data is NOT changing.
  • Tom
    Tom about 14 years
    @Erickson: I too have the same question as Amir. ;-) Why is it dangerous?
  • brady
    brady about 14 years
    I'd encourage you to read some articles by Brian Goetz. There is a section in this one that might explain the problem better than I can: ibm.com/developerworks/java/library/j-jtp08223/index.html#2.‌​0
  • brady
    brady about 14 years
    @Amir Afghani - constructing the object in the first place is a "change" that needs to be visible to other threads. Here is another Brian Goetz article about safe construction: ibm.com/developerworks/java/library/j-jtp0618.html
  • james
    james about 14 years
    Yep, and here's a good one on using volatile for safe publication: ibm.com/developerworks/java/library/j-jtp06197.html
  • brady
    brady about 14 years
    See also the "Visibility Hazards" section: ibm.com/developerworks/java/library/j-jtp0618.html#1c
  • brady
    brady about 14 years
    The most informative explanation is the Java Language Specification, Chapter 17 (java.sun.com/docs/books/jls/third_edition/html/memory.html)‌​. But Brian Goetz's summary (ibm.com/developerworks/java/library/j-jtp0618.html#1c) may be more to-the-point. Note that in his example, "the data is not changing", but it's still not safe.
  • Jon M
    Jon M about 14 years
    Fascinating, thanks erickson. That's exactly the kind of gotcha' I was afraid of. My reading of Pattern #2 on this page: ibm.com/developerworks/java/library/j-jtp06197.html confirms your suggestion to build the tree and then have the read threads access it through a volatile variable is a really good idea. Thanks!
  • Jon M
    Jon M about 14 years
    & even better the crunch point already exists in my code. This will be a one-keyword fix :)