开发者

Safe publication producer consumers scenario

开发者 https://www.devze.com 2023-04-13 01:59 出处:网络
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

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 doubles or longs) but in Java 5 and later where the double word problem is fixed, you're solid.


  1. 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 after setSharedObject is invoked (this includes the write of data).

    That being said, if the producer writes SharedObject B and data b, and the consumer sees SharedObject A they will not see data b. Though if the consumer see SharedObject B they are garunteed to see data b

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

0

精彩评论

暂无评论...
验证码 换一张
取 消