Safe publication producer consumers scenario
I'm trying to find the fastest possible and lock free solution for one producer-many consumers scenario. I assume that when dealing with only one producer and values can be lost in between we need to care about safe publication only. I came out with the following solution concept:
// effectively immutable
class SharedObject {
private double data;
public double getData() {
return data;
}
public void setData(double data) {
this.data = data;
}
}
// object being shared between producent and consumers
class Holder {
private volatile SharedObject sharedObject = new SharedObject();
public SharedObject getSharedObject() {
return sharedObject;
}
public void setSharedObject(SharedObject sharedObject) {
this.sharedObject = sharedObject;
}
}
class Producer extends TimerTask {
private final Holder holder;
public Producer(Holder holder) {
this.holder = holder;
}
@Override
public void run() {
// produce new object
SharedObject so = new SharedObject();
so.setData(Math.random());
// from now on 'so' object is effectively immutable
// publish it
holder.setSharedObject(so);
}
}
class Consumer extends TimerTask {
private Holder holder;
public Consumer(Holder holder) {
this.holder = holder;
}
@Override
public void run() {
// do something with the newest value - current snapshot (save, send etc.)
System.out.println(holder.getSharedObject());
}
}
public class Main {
public static void main(String[] args) throws Exception {
Holder holder = new Holder();
Timer timer = new Timer();
Producer producer = new Producer(holder);
timer.scheduleAtFixedRate(producer, 0, 10);
Consumer c1 = new Consumer(holder);
timer.scheduleAtFixedRate(c1, 0, 10);
Consumer c2 = new Consumer(holder);
tim开发者_Python百科er.scheduleAtFixedRate(c2, 0, 10);
}
}
The idea is based on the statement from Java Concurrency in Practice
Effectively immutable objects must be safely published
1) Is this solution "safe" for my needs? (consumers will be able to read the newest value of the SharedObject reference and its data inside, values can be lost in between)
2) Do you know more optimal solutions?
3) If we care only about newest snapshots of the object, does this mean that we can have multiple producers?
Effectively immutable objects must be safely published
SharedObject
is not immutable so is not safe.
If you want SharedObject
instances to be immutable you should define it thus
class SharedObject {
public final double data;
SharedObject(double data) { this.data = data; }
}
That will guarantee that no setData
can happen-before or after a getData
so no need for the changes to the internal data property to be propagated cross-thread.
1) Is this solution "safe" for my needs? (consumers will be able to read the newest value of the SharedObject reference and its data inside, values can be lost in between)
The volatile
on the holder's shared object should mean that holder.sharedObject
is always up-to-date when read.
3) If we care only about newest snapshots of the object, does this mean that we can have multiple producers?
The word "newest" implies some sense of "happens-before" -- the consumer doesn't see the production of values that happened before the production of the newest value. Where is that enforced?
EDIT:
Ok. I think I understand the problem now. You are relying on volatile
to present not only the right object, but the right view of the object. This is valid in Java 5 and later, but not before.
From http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml
As of Java 5, accessing a volatile variable creates a memory barrier: it effectively synchronizes all cached copies of variables with main memory, just as entering or exiting a synchronized block that synchronizes on a given object. Generally, this doesn't have a big impact on the programmer, although it does occasionally make volatile a good option for safe object publication.
So since the field that holds the reference to the SharedObject
is volatile, the consumer will always see the most recent (for some definition) value stored in the SharedObject
.
This seems to fit the requirements you laid down.
So under Java 4 and before, it would probably work -- as long as SharedObject
contains a single-word field (no double
s or long
s) but in Java 5 and later where the double word problem is fixed, you're solid.
This implementation as written implements safe publication. The reason this is true is because of the volatile store of
public void setSharedObject
. Since the SharedObject field is volatile all writes that occur prior to this store will be visible aftersetSharedObject
is invoked (this includes the write ofdata
).That being said, if the producer writes
SharedObject B
anddata b
, and the consumer seesSharedObject A
they will not seedata b
. Though if the consumer seeSharedObject B
they are garunteed to seedata b
- Volatile store is about 1/3 total time of a monitor store so I think this is pretty fast
Edit to answer your question:
I basically need a confirmation that "effectively immutable" objects and all the other internal fields in those objects, both primitives and other references, will be immediately visible. (assuming that the SharedObject and it's internals will never be modified after publication).
Yes, this is correct. All writes that occur in invoking thread before the volatile write occurs will be visible to any calling thread that see's the volatile write take effect. So if the SharedObject is visible then the SharedObject data wil be visible.
精彩评论