Use volatile to ensure the visibility of shared (but not concurrent) data in Java
I'm trying to implement a fast version of LZ77. I have a question for you about concurrent programming
Now I have a final byte [] buffer and a final int [] resultholder, both of which have the same length The plan does the following:
>The main thread writes all buffers, then notifies the threads and waits for them to complete. > A single worker thread processes a portion of the buffer and stores the result in the same portion of the result holder The workers' part is exclusive After that, the main thread is notified and the worker pauses. > When all workers pause, the main thread reads the data in the resultholder and updates the buffer, and then (if necessary) the process starts from point 1 again
The important items in the manager (main thread) are declared as follows:
final byte[] buffer = new byte[SIZE]; final MemoryHelper memoryHelper = new MemoryHelper(); final ArrayBlockingQueue<Object> waitBuffer = new ArrayBlockingQueue<Object>(TOT_WORKERS); final ArrayBlockingQueue<Object> waitResult = new ArrayBlockingQueue<Object>(TOT_WORKERS); final int[] resultHolder = new int[SIZE];
Memoryhelper just wraps a volatile field and provides two methods: one for reading it and the other for writing it
Worker's run() Code:
public void run() { try { // Wait main thread while(manager.waitBuffer.take() != SHUTDOWN){ // Load new buffer values manager.memoryHelper.readVolatile(); // Do something for (int i = a; i <= b; i++){ manager.resultHolder[i] = manager.buffer[i] + 10; } // Flush new values of resultHolder manager.memoryHelper.writeVolatile(); // Signal job done manager.waitResult.add(Object.class); } } catch (InterruptedException e) { } }
Finally, an important part of the main thread:
for(int i=0; i < 100_000; i++){ // Start workers for (int j = 0; j < TOT_WORKERS; j++) waitBuffer.add(Object.class); // Wait workers for (int j = 0; j < TOT_WORKERS; j++) waitResult.take(); // Load results memoryHelper.readVolatile(); // Do something processResult(); setBuffer(); // Store buffer memoryHelper.writeVolatile(); }
The synchronization effect on arrayblockingqueue is very good I suspect using readvolatile() and writevolatile() I was told that writing a volatile field would refresh all previously changed data, and then reading it from another thread would make them visible
Is it sufficient to ensure correct visibility in this case? There has never been a real concurrent access to the same memory area, so the volatile field should be much cheaper than readwritelock
Solution
You don't even need volatile because blockingqueues already provides the necessary memory visibility:
In general, if you have some kind of synchronization, you may not need to do anything special to ensure memory visibility because it is guaranteed by the synchronization primitive you use
However, when you do not have explicit synchronization (for example, in a lockless algorithm), volatile reads and writes can be used to ensure memory visibility
P. S.
In addition, it seems that you can use cyclicbarrier instead of your queue solution, which is specially designed for similar scenarios