我正在寻找一个可以协助以下用例的并发对象:
有什么建议吗?我可以使用ReentrantReadWriteLock
,但我有点担心阻止发布商。我宁愿让出版商破坏读者阅读的机会,也不愿让读者能够阻止出版商。
发布商帖子:
PublisherSignal ps = new PublisherSignal();
publishToAllReaders(ps.getReaderSignal());
...
while (inLoop())
{
ps.beginEdit();
data.setSomething(someComputation());
data.setSomethingElse(someOtherComputation());
ps.endEdit();
doOtherStuff();
}
读者主题:
PublisherSignal.Reader rs = acquireSignalFromPublisher();
...
while (inLoop())
{
readDataWhenWeGetAChance();
doOtherStuff();
}
...
public readDataWhenWeGetAChance()
{
while (true)
{
rs.beginRead();
useData(data.getSomething(), data.getSomethingElse());
if (rs.endRead())
{
// we get here if the publisher hasn't done a beginEdit()
// during our read.
break;
}
// darn, we have to try again.
// might as well yield thread if appropriate
rs.waitToRead();
}
}
编辑在更高级别,我要做的是让发布者每秒更改数据几千次,然后让读者以更慢的速度显示最新更新(5 -10次/秒)。我会使用ConcurrentLinkedQueue来发布更新已发生的事实,除了(a)同一项目可能有数百个更新,我想合并,因为必须重复复制大量数据似乎像废物是一个性能问题,并且(b)有多个读者似乎排除了一个队列...我想我可以有一个主代理阅读器并让它通知每个真正的读者。
答案 0 :(得分:1)
为什么不使用BlockingQueue?
您的发布者可以独立于正在阅读的内容写入此队列。读者(类似地)可以从队列中取出内容而不用担心阻止编写器。线程安全由队列处理,因此2个线程可以写入/读取而无需进一步同步等。
来自链接的文档:
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while(true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
答案 1 :(得分:0)
嗯...我想我的绊脚石围绕共享数据结构本身......我一直在使用像
这样的东西 public class LotsOfData
{
int fee;
int fi;
int fo;
int fum;
long[] other = new long[123];
/* other fields too */
}
发布商经常更新数据,但一次只更新一个字段。
听起来好像我应该找到一种以有利于使用生产者 - 消费者队列的方式序列化更新的方法:
public class LotsOfData
{
enum Field { FEE, FI, FO, FUM };
Map<Field, Integer> feeFiFoFum = new EnumMap<Field, Integer>();
long[] other = new long[123];
/* other fields too */
}
然后将更改项目发布到队列,如(FEE,23)为feeFiFoFum字段,以及(33,1234567L)为other
数组。 (出于性能原因,Bean类型的反射几乎可以肯定。)
尽管如此,看起来我只是让出版商写下他们想要的东西,并且知道有时间(最终)让读者进入并得到一致的集合,这似乎让我感到羞愧。数据,如果它只有一个标志,它可以用来判断数据是否已被修改。
更新:有趣的是,我尝试了这种方法,使用一个Muturrent对象的ConcurrentLinkedQueue(只存储1次更改所需的状态),类似于上面的第一个LotsOfData(4个int字段和一个27个longs的数组,以及在大约10000个批次之间使用Thread.sleep(1)产生总共1000万个突变的生产者,以及每100毫秒检查一次队列的消费者,并消耗任何存在的突变。我以多种方式进行测试:
因此创建每个变异对象平均为230nsec,平均为770nsec将每个变异对象排入/出列到生产者的队列中并在消费者中将其拉出(执行原始类型的突变的时间似乎可以忽略不计,与对象创建和队列操作相比,它应该是)。我猜不错,它为我提供了一些估算这种方法的性能成本的指标。