Java 安全出版物生产者-消费者场景

Java 安全出版物生产者-消费者场景,java,multithreading,concurrency,thread-safety,producer-consumer,Java,Multithreading,Concurrency,Thread Safety,Producer Consumer,我正试图为一个生产者多个消费者的场景找到尽可能快且无锁的解决方案。我假设,当只与一个生产者打交道时,价值可能会在两者之间消失,我们只需要关心安全出版。我提出了以下解决方案概念: // effectively immutable class SharedObject { private double data; public double getData() { return data; } public void setData(double

我正试图为一个生产者多个消费者的场景找到尽可能快且无锁的解决方案。我假设,当只与一个生产者打交道时,价值可能会在两者之间消失,我们只需要关心安全出版。我提出了以下解决方案概念:

// 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);
        timer.scheduleAtFixedRate(c2, 0, 10);
    }
}
这个想法是基于Java并发性在实践中的声明

必须安全地发布有效的不可变对象

1此解决方案对我的需求安全吗?消费者将能够读取SharedObject引用的最新值及其内部数据,值可能会在其间丢失

2你知道更多的最佳解决方案吗

3如果我们只关心对象的最新快照,这是否意味着我们可以有多个生产者

必须安全地发布有效的不可变对象

SharedObject不是不可变的,因此不安全

如果希望SharedObject实例是不可变的,那么应该这样定义它

class SharedObject {
    public final double data;
    SharedObject(double data) { this.data = data; }
}
这将保证在getData之前或之后不会发生setData,因此无需跨线程传播对内部数据属性的更改

1此解决方案对我的需求安全吗?消费者将能够读取SharedObject引用的最新值及其内部数据,值可能会在其间丢失

holder的共享对象上的volatile应该意味着holder.sharedObject在读取时始终是最新的

3如果我们只关心对象的最新快照,这是否意味着我们可以有多个生产者

“最新”一词意味着某种意义上的“以前发生过”——消费者看不到在生产最新价值之前发生的价值生产。在哪里强制执行

编辑:

嗯。我想我现在明白这个问题了。您依赖volatile不仅显示正确的对象,而且显示对象的正确视图。这在Java5和更高版本中是有效的,但在以前不是

从Java5开始,访问易失性变量会造成内存障碍:它会有效地将变量的所有缓存副本与主内存同步,就像进入或退出在给定对象上同步的同步块一样。一般来说,这对程序员没有太大的影响,尽管它偶尔会使volatile成为安全对象发布的一个好选择

因此,由于保存对SharedObject的引用的字段是易变的,消费者将始终看到存储在SharedObject中的某个定义值的最新值

这似乎符合你提出的要求

因此,在Java4及之前的版本中,它可能会起作用——只要SharedObject包含一个单词字段,而不是double或long,但在Java5及更高版本中,如果双单词问题得到解决,那么就可以了

此书面实现实现了安全发布。这个 之所以如此,是因为公共空间的不稳定储存 设置共享对象。由于SharedObject字段是可变的 在此存储之前发生的写入将在之后可见 调用SetShareObject,包括数据写入

也就是说,如果制作人编写SharedObject B和数据 b、 消费者看到SharedObject A时,他们不会看到数据 B不过,如果消费者看到SharedObject B,他们就会受到指责 查看数据b

Volatile存储大约是监视器存储总时间的1/3,所以我认为 这相当快 编辑以回答您的问题:

我基本上需要确认有效的不可变对象 以及这些对象中的所有其他内部字段,都是基本体 和其他参考,将立即可见。假设 SharedObject及其内部结构将在 出版


是的,这是正确的。在易失性写入发生之前,调用线程中发生的所有写入都将对任何调用线程可见,以确保易失性写入生效。因此,如果SharedObject是可见的,那么SharedObject数据将是可见的。

这个问题与非阻塞无关,只是提到对象不是不变的,也不是可变的。SharedObject实际上是不可变的,这意味着在使它对其他线程可见之后,对象状态永远不会改变。3最新的方式在其他方面,他们到达,不依赖于实际的逻辑。@alphashock,Ok,不是一成不变的,也不是在分享后变异的。我的观点是,它们的到达没有明确的顺序,所以说它们是最新的毫无意义。你能说的最好的话就是,没有价值会被同一个消费者两次消费,没有价值会被从未生产过的消费,对于一对消费者来说,消费者不会消费同一个生产者生产的无序价值。@alphashock,其实我错了。一个消费者可以消费相同的价值两次,因为在消费上没有价值的归零。让我们忘记多个生产者吧-
只有一个。我的问题是允许读取相同的值两次。安全发布的有效不可变对象可以被任何线程安全地使用,而无需额外的同步。关于内存可见性,我想我知道你对final的建议绝对是安全的,但我认为这一个也是安全的,即使SharedObject中有更复杂的字段。当对象实际上是不可变的时,所有访问同一Holder实例的线程都将能够读取最新值,如果没有发生更新,则能够读取最后一个值。@alphashock,请参阅我的编辑。只要您不关心双重消耗,并且在JDK1.5或更高版本上运行,您就应该是好的。SharedObject A和B是什么意思。一旦设置了对象B上的数据,共享对象A是发布新SharedObject B之前的前一个对象吗?我基本上需要确认有效的不可变对象和这些对象中的所有其他内部字段,包括原语和其他引用,将立即可见。假设SharedObject及其内部构件在发布后永远不会被修改。