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
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.
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’.