我正在编写一个模拟书籍市场的应用程序。
考虑一本名为" BookA"其中包含以下数据:
45 47 50 51 55 70 73 75 79 81
**Bookstore1 Buy Qty** 2 3 5 11 1
**Bookstore2 Buy Qty** 1 3 5 1 10
**Bookstore1 Sell Qty** 1 11 7 8 20
**Bookstore2 Sell Qty** 2 5 2 5 10
**Data for BookA**
数值数据存储为volatile int[][] dataStorage = new int[5][10]
,其中row [0]包含价格。
row [1] ..row [4]包含每个书店的数量。 例如,row [1] col [0]表示在45美元,Bookstore1愿意购买2份BookA。同样,第[4] [5]行表明,Bookstore2售价为70美元,愿意出售2份。
我的市场上有大约500本书,每种书的数据都存储在ConcurrentHashMap中:
(ConcurrentMap<String, int[][]> map = new ConcurrentHashMap<String, int[][]>(500)
)
&#34; BookA&#34; ----&GT; INT [] []
&#34; bookN的&#34; ----&GT; INT [] []
来自Bookstore1和Bokstore2的数据到达两个独立的线程。目前,我将原始数据对象存储在阻塞队列中,并使用单个线程(&#34; ProcessingThread&#34;)来创建和修改(如果有更新)上述数组。
最后,我有一些客户,他们在一个单独的帖子中(&#34; CustomerThread&#34;)向我发送购买/出售书籍的订单。
典型的订单如下:&#34;以50美元购买BookA的3&#34;:
收到订单后,我会执行以下操作:
1)检查地图是否包含密钥&#34; BookA&#34;。
2)如果是,那么我在拿着 ReadLock (ReentrantReadWriteLock.ReadLock)时克隆BookA的数据(int [] [])。
3)然后我迭代克隆的数据以找到价格和总数量。
我的问题是:
a)有人可以确认我不需要同步生产者(&#34; ProcessingThread&#34;)。由于dataStorage(int [] [])仅由&#34; ProcessingThread&#34;线。此外,由于dataStorage是易变的,因此&#34;发生之前&#34;将在我写信时建立(因此,&#34; CustomerThread&#34;将看到最新更新的数据)。
b)是否有更好的(比使用锁定更具可扩展性)方式来确保&#34; CustomerThread&#34 ;?中的线程安全性?我可以通过使用AtomicIntegerArray逃脱吗?
由于
答案 0 :(得分:1)
我在这里看到一点麻烦。你的数组是易变的,但元素不是。只有一个线程修改元素,你就安全了。但您无法保证CustomerThread会看到元素更改。此外,即使元素本身 易变(不容易),数组也会发生变化,而且CustomerThread很容易看到不完整的数据。
第一个解决方案是对每个阵列进行适当的锁定。然后,CustomerThread必须等到完整阵列准备就绪。 (多个CustomerThreads可以解决一本书阻止对其他书籍采取行动的问题,我认为这不会使你的并发问题变得更糟。)
另一种解决方案是替换整个数组而不是修改它们。数组变得有效不可变。从一组数据到下一组数据的变化是即时的,并且数据始终是一致的。现在,数组引用是易失性的这一事实确保了CustomerThread立即可见的更改,现在永远不必等待锁定。
我喜欢第二种解决方案,它更像是回答你的问题B.(原子药往往价格昂贵。如果你可以用一个原子替换一个锁,你就会领先于游戏,但如果你要替换一个用100个原子锁定,甚至只用10个,你就在后面。)
但是,我有点担心多个订单的结果。如果剩下一本书,你不想把它卖给5个不同的人。在我看来,根据您所写的内容,CustomerThread上的订单和从阻塞队列进入ProcessingThread的信息需要仔细同步。 ProcessingThread和CustomerThread都是一个线程,因此它们可以单独使用。但他们可以同时做一些事情,比如放弃可用的书籍数量并订购一些书籍。这两个事件需要按顺序发生,所以我们知道谁得到了这些书,而正式的Java同步块让我觉得这是最好的方法。
但是你最了解你要做的事情(我对书店放入队列的数据有点困惑)。希望如果它实际上没有回答你的问题,这会给你一些信息。