我正在努力为一个生产者 - 许多消费者场景找到最快和无锁解决方案。我假设当只处理一个生产者并且价值可能在两者之间丢失时,我们只需关心安全发布。我提出了以下解决方案概念:
// 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 Concurrency in Practice
中的声明必须安全发布有效的不可变对象
1)这个解决方案对我的需求“安全”吗? (消费者将能够读取SharedObject引用及其数据的最新值,值之间可能会丢失)
2)您是否了解更优化的解决方案?
3)如果我们只关心对象的最新快照,这是否意味着我们可以拥有多个生产者?
答案 0 :(得分:0)
必须安全发布有效的不可变对象
SharedObject
不是不可变的,所以不安全。
如果您希望SharedObject
个实例是不可变的,那么您应该定义它
class SharedObject {
public final double data;
SharedObject(double data) { this.data = data; }
}
这将保证在setData
之前或之后不会发生getData
,因此不需要跨内核传播内部数据属性的更改。
1)这个解决方案对我的需求“安全”吗? (消费者将能够读取SharedObject引用及其数据的最新值,值之间可能会丢失)
持有人的共享对象上的volatile
应该意味着holder.sharedObject
在阅读时始终是最新的。
3)如果我们只关心对象的最新快照,这是否意味着我们可以拥有多个生产者?
“最新”一词意味着某种“先发生过”的感觉 - 消费者看不到在产生最新价值之前发生的价值的产生。强制执行的地方在哪里?
编辑:
确定。我想我现在明白了这个问题。您依靠volatile
不仅可以呈现正确的对象,还可以呈现对象的右视图。这在Java 5及更高版本中有效,但之前没有。
来自http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml
从Java 5开始,访问volatile变量会创建一个内存屏障:它有效地将所有缓存的变量副本与主内存同步,就像进入或退出在给定对象上同步的同步块一样。一般来说,这对程序员没有太大的影响,尽管它偶尔会使volatile成为安全对象发布的良好选择。
因此,保持对SharedObject
的引用的字段是易变的,所以消费者将始终看到存储在SharedObject
中的最新(对于某些定义)值。
这似乎符合您的要求。
因此,在Java 4及之前,它可能会起作用 - 只要SharedObject
包含单字段字段(无double
s或long
s),但在Java 5中后来双字问题得到解决,你就是稳固的。
答案 1 :(得分:0)
这个书面实现实现了安全发布。该
这是正确的原因是因为public void
setSharedObject
的易变性存储。由于SharedObject字段是volatile所有
在此商店之前发生的写入将在之后可见
调用setSharedObject
(这包括写data
)。
话虽如此,如果制作人写SharedObject B
和data
b
,并且消费者看到SharedObject A
他们将看不到data
b
。但如果消费者看到他们是SharedObject B
,他们就会受到谴责
查看data b
编辑以回答您的问题:
我基本上需要确认“有效不可变”的对象 和这些对象中的所有其他内部字段,都是基元 和其他参考,将立即可见。 (假设是 SharedObject及其内部将永远不会被修改 公报)。
是的,这是正确的。在发生易失性写入之前调用线程中发生的所有写入对任何看到易失性写入生效的调用线程都是可见的。因此,如果SharedObject可见,那么SharedObject数据将是可见的。