6

I tried to search but couldn't find exact answer I was looking for hence putting up a new question.

If you wish to share any mutable object(s) between multiple threads, are there any best practices/principles/guidelines to do it ?

Or will it simply vary case by case ?

3
  • 1
    I would start reading the Oracle documentation on concurrency. docs.oracle.com/javase/tutorial/essential/concurrency/…, especially synchronization and liveness Commented Feb 28, 2017 at 12:30
  • 1
    There are whole books on the topic of Java multi-threading, and one of the things that they cover is how to safely share objects. There is no single "best practice". Commented Feb 28, 2017 at 13:05
  • This one is worth reading: shipilev.net/blog/2014/jmm-pragmatics Commented Feb 28, 2017 at 13:07

5 Answers 5

8

Sharing mutable objects between threads is risky.

The safest way is to make the objects immutable, you can then share them freely.

If they must be mutable then each of the objects each needs to ensure their own thread safety using the usual methods to do so. (synchronized, AtomicX classes, etc).

The ways to protect the individual objects will vary a lot though depending on how you are using them and what you are using them for.

Sign up to request clarification or add additional context in comments.

Comments

5

In java, you should synchronize any method that changes/reads the state of shared object, it is the easiest way.

other strategies are:

  • make use of thread safe classes (ConcurrentHashMap) for example
  • use of locks
  • use of volatile keyword, to avoid stale objects (sometimes could be used as lightweight synchronizer)

they key is sync your updates/reads to guarantee consistent state, the way you do it, could vary a lot.

Comments

2

The problems with sharing objects between threads are caused by having the two threads access the same data structure at the same time, with one mutating the structure while the other depends on the structure to be complete, correct or stable. Which of these cause the problem is important and should be considered when choosing the strategy.

These are the strategies I use.

  1. Use immutable objects as much as possible.

This removes the issue of changing the data structure altogether. There are however a lot of useful patterns that can not be written using this approach. Also unless you are using a language/api which promotes immutability it can be inefficient. Adding a entry to a Scala list is much faster than making a copy of a Java list and adding a entry to the copy.

  1. Use the synchronize keyword.

This ensures that only one thread at a time is allowed to change the object. It is important to choose which object to synchronize on. Changing a part of a structure might put the hole structure in an illegal state until another change is made. Also synchronize removes many of the benefits of going multithreaded in the first place.

  1. The Actor model.

The actor model organizes the world in actors sending immutable messages to each other. Each actor only has one thread at once. The actor can contain the mutability. There are platforms, like Akka, which provide the fundamentals for this approach.

  1. Use the atomic classes. (java.util.concurrent.atomic)

These gems have methods like incrementAndGet. They can be used to achieve many of the effects of synchronized without the overhead.

  1. Use concurrent data structures.

The Java api contains concurrent data structures created for this purpose.

  1. Risk doing stuff twice.

When designing a cache it is often a good idea to risk doing the work twice instead of using synchronize. Say you have a cache of compiled expressions from a dsl. If an expression is compiled twice that is ok as long as it eventually ends up in the cache. By allowing doing some extra work during initialization you may not need to use the synchronize keyword during cache access.

2 Comments

This answer is simplistic. Two threads mutating data at the same time (race conditions) is only one of the possible problems you can find. You can also have issues related to the proper publication of values when using a shared structure, if you have one single thread writing and others reading from such a shared structure, the reading threads may not see the changes happening from the writing thread, even if reading later in time.
It is not simplistic, just poorly worded in the first sentence.(I will rephrace) The strategies I describe are still valid and I think I make some good points that should be a part of the discussion. I do not claim to solve all possible problems, rather I propose some useful patterns to create pragmatic and robust multitreaded applications.
1

There is example. StringBuilder is not thread safe, so without synchronized (builder) blocks - result will be broken. Try and see.

Some objects are thread safe (for example StringBuffer), so no need to use synchronized blocks with them.

    public static void main(String[] args) throws InterruptedException {
        StringBuilder builder = new StringBuilder("");

        Thread one = new Thread() {
            public void run() {

                for (int i = 0; i < 1000; i++) {
                    //synchronized (builder) {
                        builder.append("thread one\n");
                    //}
                }
            }
        };
        Thread two = new Thread() {
            public void run() {

                for (int i = 0; i < 1000; i++) {
                    //synchronized (builder) {
                        builder.append("thread two\n");
                    //}
                }
            }
        };
        one.start();
        two.start();
        one.join();
        two.join();

        System.out.println(builder);
    }

Comments

1

Although there are some good answers already posted, but here is what I found while reading Java Concurrency in Practice Chapter 3 - Sharing Objects.

Quote from the book.

The publication requirements for an object depend on its mutability:

  • Mutable objects can be published through any mechanism;
  • Effectively immutable objects (whose state will not be modified after publication) must be safely published;
  • Mutable objects must be safely published, and must be either threadsafe or guarded by a lock.

Book states ways to safely publish mutable objects:

To publish an object safely, both the reference to the object and the object's state must be made visible to other threads at the same time. A properly constructed object can be safely published by:

  • Initializing an object reference from a static initializer;
  • Storing a reference to it into a volatile field or AtomicReference;
  • Storing a reference to it into a final field of a properly constructed object; or
  • Storing a reference to it into a field that is properly guarded by a lock.

The last point refers to using various mechanisms like using concurrent data structures and/or using synchronize keyword.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.