0

I've read about the 'status flag' pattern for the volatile usage. It says that I can use the volatile without any sync if the status flag doesn't depend on any other state. It will guarantee the visibility of the flag for other threads. Moreover, write to the boolean is atomic.

But in the other related question it's said that it's safe to use the volotile when only one thread can modify the flag. Otherwise, I need to use any synchronization or AtomicBoolean.

In my example, I have the stopped flag, but it could be modified more than from within one thread: methods stop() and continue(). The doSmth() doesn't update any states. If assume that it's OK not to do work when the stop() was invoked right after the continue() method, would be the code threadsafe?

class MyClass {
    private volatile boolean stopped;

    public void doWork() {
        while(!stopped) {
            doSmth();
        }
    }

    public void stop() {
        stopped = true;
    }

    public void continue() {
        stopped = false;
    }
}

As for me, it should. Could you please clarify if I'm wrong?

androberz
  • 744
  • 10
  • 25
  • You're not using volatile. Nobody know what `doSmth` is. If you just have a boolean variable 'stopped', sure it's thread safe. – matt Jul 10 '19 at 09:03
  • Code looks fine. Where exactlty did you read that `volatile` does not work for this use-case? The accepted answer on the thread you linked posits exactly your scenario as a good use for `volatile`. – Thilo Jul 10 '19 at 09:31
  • You are right, I missed the volatile modifier in the example. The doSmth() is just do something not related to the flag. I've updated the description. – androberz Jul 10 '19 at 09:35
  • @matt, you wrote: If you just have a boolean variable 'stopped', sure it's thread safe. Non-volatile boolean doesn't guarantee the visibility by other threads and I could have the infinite loop int the doWork(). I believe it couldn't be threadsafe. – androberz Jul 10 '19 at 09:39
  • @Thilo, I've updated the link to the answer. As I understood from it, if we have modification from > 1 threads, it might not be thread-safe. Also, it wasn't my statement, it's more a question if I understood right. – androberz Jul 10 '19 at 09:44
  • Well, that just says that it does not have `compareAndSet` and friends if you want to do more complex things. But you don't need that here. – Thilo Jul 10 '19 at 09:57
  • "Non-volatile boolean doesn't guarantee..." are you asking about volatile or non-volatile? "I could have the infinite loop int the doWork()." You dont' have any loop. If you don't make it volatile, then another thread could see stale values of the boolean, and you could get an 'infinite' loop as there is no guarantee when the updated value will be available for all threads. Volatile means you will always see the freshest value. That doesn't mean your class is thread safe in general, eg you might need to check and set in one operation (atomically). This wouldn't guarantee that. – matt Jul 10 '19 at 10:51

1 Answers1

2

volatile simply ensures that changes to the variable are available to all threads.

The background: a thread may make local copies of shared variables. Synchronizing the values of these local variables with the global shared variables is what volatile effects.

However that does not synchronize in the java sence of a single entry, monitor/critical region.

The entire toolchest of java.util.concurrent offers things like ensuring that only one thread may change the value and such. If you want to start from ground up, one can with two variables do some blocking things: search for Dijkstra algorithms.

Here I think AtomicBoolean might be nice for non-blocking usage.


If you want to achieve to have a global boolean state that pauses resp. resumes threads when toggled (your stopped), instead of some ugly busy wait:

public void run () {
    while (true) {
        doWork(); 
        try {
            barrier.await();
        } catch (InterruptedException | BrokenBarrierException ex) {
            return;
        }
    }
}

Using a global CyclicBarrier - not the nicest API as it works with N predefined Runnables.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    For a simple "Thread should stop" flag as in this case, `volatile boolean` appears sufficient. It is not blocking, either. But yes, in general this kind of code is way too low-level. No one should be managing threads directly these days. Is there something simple in concurreny utils for "repeat runnable until I say stop"? – Thilo Jul 10 '19 at 10:00
  • @Joop Eggen, thanks for your answer. My thoughts are that I don't have any critical section here except primitive boolean flag. But since the boolean read/write are atomic, I don't have to make any synchronization here, even if I modify it from many threads. The volatile will make each thread to see the value of the volatile boolean from the main memory. Please let me know if I'm wrong. – androberz Jul 10 '19 at 10:15
  • @Thilo `thread.interrupt()` or `threadGroup.interrupt()` for a friendly ending. Then [`ExecuterService.shutdown, shutdownNow, awaitTermination`](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/ExecutorService.html) – Joop Eggen Jul 10 '19 at 11:15
  • 1
    @androberz If `doWork` is what `run` does, then the thread would end definitely after the first `stopped` being set to `true`; I'll add code. – Joop Eggen Jul 10 '19 at 11:18