0

I code in Java8, and I want to use a thread and then catch the result which the thread creates via a mutable object. The code is something like the following:

public class MyTest {

    public static void main(String[] args) throws InterruptedException {
        MutableString mutableString = new MutableString();

        Thread thread = new Thread(
                () -> mutableString.value = "value_from_another_thread");
        thread.start();
        thread.join();

        assert "value_from_another_thread".equals(mutableString.value);
    }

    private static class MutableString {
        String value; // Does this have to be volatile or should I use AtomicReference instead of MutableString?
    }
}

It seems that it works fine on my dev laptop, but is there any possibility where this code doesn't work properly in some environment? Should I use volatile for the value field or replace the MutableString class by AtomicReference for safety?

EDIT:

What if a CountDownLatch was used instead of thread.join() ? Does it change something?

public class MyTest {

    public static void main(String[] args) throws InterruptedException {
        MutableString mutableString = new MutableString();
        CountDownLatch latch = new CountDownLatch(1);
        MyThread myThread = new MyThread(latch, mutableString);

        myThread.start();
        latch.await();

        assert "value_from_another_thread".equals(mutableString.value);
    }

    private static class MyThread extends Thread {

        private final CountDownLatch latch;
        private final MutableString mutableString;

        MyThread(CountDownLatch latch, MutableString mutableString) {
            this.latch = latch;
            this.mutableString = mutableString;
        }

        @Override
        public void run() {
            mutableString.value = "value_from_another_thread";
            latch.countDown();
        }
    }

    private static class MutableString {
        String value; // Does this have to be volatile or should I use AtomicReference instead of MutableString?
    }
}
Kohei Nozaki
  • 1,154
  • 1
  • 13
  • 36

2 Answers2

4

There is a happens before relation between calling the thread stopping and the join completing. So the join will see whatever the thread has done. Hence no extra synchronization is needed.

https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.4

Using a countdown latch provides similar ordering guarantees. https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html Look for memory consistency effects.

Personally I would not try to be too smart. Unless you are writing very high performant code and you have some tight loop with a volatile write, adding a volatile will not make much performance impact on your application. So I would rather be conservative and add volatile.

pveentjer
  • 10,545
  • 3
  • 23
  • 40
1

You have to do something.

volatile would be one way, synchronized another. Both very low-level. Maybe join also already guarantees this. I don't want to have to think about this, and you normally don't have to by using the proper higher-level abstractions.

If those were the only options (we were not allowed to do any refactoring at all), I'd say go with AtomicReference. It's made for exactly this use-case and gives you more power (such as compare-and-swap).

However: Why have mutable state at all?

Replace the Runnable inside your Thread with a Callable<String> and have it return its result as a return value.

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • +1 for info about `join()` - I didn't know that. What if it was `CountDownLatch` then (question edited) ? Thanks for the other suggestions too – Kohei Nozaki Jul 04 '20 at 01:39
  • 1
    I don't want to have to think about this, and you normally don't have to by using the proper higher-level abstraction, which in this case is `Callable`. – Thilo Jul 04 '20 at 03:01