Question about using "volatile" keyword in SiO2 Job

Hi,

I am using SiO2 WorkerPool and Job for running pathfinding tasks in a background thread.

    private class PathFindingJob implements Job {

        private final NavigationSpace navSpace;
        private final Vector3f from;
        private final Vector3f to;
        private final double radius;
        private final Consumer<LinePath<Vector3>> consumer;

        private LinePath<Vector3> path;

        public PathFindingJob(NavigationSpace navSpace, Vector3f from, Vector3f to, double radius, Consumer<LinePath<Vector3>> consumer) {
            this.navSpace = navSpace;
            this.from = from;
            this.to = to;
            this.radius = radius;
            this.consumer = consumer;
        }

        @Override
        public void runOnWorker() {
            path = findPath(navSpace, from, to, radius, worldSize);
        }

        @Override
        public double runOnUpdate() {
            consumer.accept(path);
            return 1;
        }
    }

runOnWorker and runOnUpdate methods will be called from different threads.

runOnWorker is always called first by the worker thread, then runOnUpdate will be called later on by the game loop thread.

Do I need to use the volatile keyword for the path field in the above code?

Thanks in advance

1 Like

On java language specification from oracle, it states that the volatile keyword enables the Java Memory Model to ensure that all threads see a consistent value for the variable.

You could mark those two methods as synchronized or use a ReentrantLock, but it is far better to use a volatile keyword; because this creates a non-blocking interthread-action.

The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable

EDIT:
If you know a 100% that runOnWorker and runOnUpdate are synchronized such that the runOnWorker runs first then you don’t need to use a volatile keyword, the volatile keyword synchronizes the R/W operations performed on the variables.

3 Likes

It’s all about “memory barriers”.

Conceptually: If two threads are accessing the same data (read or write, it doesn’t matter) then they may have their own locally cached copies of that data…either because the OS has given them their own thread-specific RAM or because the CPU is using its own cache lines, whatever the case. Each thread might be seeing its own copy of the variable foo. It’s possible for thread 1 to modify it and for thread 2 not to see it until quite a bit later (in computer cycle terms).

In Java, there is something called a “memory barrier” which forces all involved threads to see the same version of the variable. The start of a synchronized block will force a memory barrier (traditionally it was only the start… this and other issues meant that “back in the day” double-checked locking would not work reliably in Java but that’s another topic).

The other way to force a consistent view is with the volatile keyword… which on its own will effectively force a memory barrier as well. (It’s sometimes possible to piggy back other field updates on top of a volatile field update but I don’t necessarily recommend it.)

As Pavl_G states, in your case it’s probably ok. Given the timings involved and the number of other threaded data structures that things run through, it’s quite likely that a memory barrier has already happened. However, with the prevalence of lock-free data structures (most of the java.util.concurrent stuff tries to be lock-free) there is no real guarantee… unless you use the volatile keyword.

And while presumably there is some expense for using the volatile keyword, it is going to be a LOT cheaper than ‘synchronized’.

6 Likes

I see, thanks a lot guys for the help

2 Likes