我正在創建一個圖形計算器。Java中的難以捉摸的競爭條件
爲了試圖從中獲得更多的性能,我在線計算器中添加了一些多線程。本質上,我目前的實現是構造X值的線程安全的Queue
,然後啓動它所需的多個線程,每個線程使用隊列計算線上的點以獲取其值,然後使用HashMap
排序點計算完成。這個實現很好,這不是我的競爭條件(僅僅是一些背景信息)的地方。
在檢查性能結果時,我發現HashMap
是一個性能瓶頸,因爲我在一個線程上同步執行。所以我認爲按照計算的順序排列每個點會效果最好。我嘗試了PriorityQueue
,但這比HashMap
慢。
我結束了創建的算法,基本上是這樣的:
我構建的X值的列表來計算,就像我現在的算法。
然後,我將該值列表複製到另一個類中,缺乏想象力並暫時命名爲BlockingList
,它負責在計算點時對點進行排序。
BlockingList
包含一個put()
方法,它採用兩個BigDecimal
s作爲參數,第一個是X值,第二個是計算出的Y值。 put()
將只接受一個值,如果X值是列表中下一個要在X值列表中接受的值,並且將阻塞,直到另一個線程給出下一個例外值。
例如,因爲這可能會令人困惑,比如我有兩個線程,Thread-1
和Thread-2
。 Thread-2
從值隊列中獲取X值10.0
,並且Thread-1
得到9.0
。但是,Thread-1
首先完成其計算,並在Thread-2
之前呼叫put()
。因爲BlockingList
預計首先得到10.0
,而不是9.0
,它將阻止Thread-1
直到Thread-2
完成並呼叫put()
。一旦Thread-2
給出BlockingList
10.0
,它notify()
所有等待的線程,並且預計接下來9.0
。這一直持續到BlockingList
獲得其所有預期值。
(我道歉,如果這是難以遵循,如果你需要更多的澄清,只是要求。)
正如預期的問題的標題,還有在這裏的比賽條件。如果我在沒有任何System.out.println
的情況下運行它,它有時會因爲與wait()
和notifyAll()
衝突而鎖定,但如果我放入println
,它將運行良好。
小實現這個包含如下,並表現出相同的行爲:
import java.math.BigDecimal;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Example {
public static void main(String[] args) throws InterruptedException {
// Various scaling values, determined based on the graph size
// in the real implementation
BigDecimal xMax = new BigDecimal(10);
BigDecimal xStep = new BigDecimal(0.05);
// Construct the values list, from -10 to 10
final ConcurrentLinkedQueue<BigDecimal> values = new ConcurrentLinkedQueue<BigDecimal>();
for (BigDecimal i = new BigDecimal(-10); i.compareTo(xMax) <= 0; i = i.add(xStep)) {
values.add(i);
}
// Contains the calculated values
final BlockingList list = new BlockingList(values);
for (int i = 0; i < 4; i++) {
new Thread() {
public void run() {
BigDecimal x;
// Keep looping until there are no more values
while ((x = values.poll()) != null) {
PointPair pair = new PointPair();
pair.realX = x;
try {
list.put(pair);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}.start();
}
}
private static class PointPair {
public BigDecimal realX;
}
private static class BlockingList {
private final ConcurrentLinkedQueue<BigDecimal> _values;
private final ConcurrentLinkedQueue<PointPair> _list = new ConcurrentLinkedQueue<PointPair>();
public BlockingList(ConcurrentLinkedQueue<BigDecimal> expectedValues) throws InterruptedException {
// Copy the values into a new queue
BigDecimal[] arr = expectedValues.toArray(new BigDecimal[0]);
_values = new ConcurrentLinkedQueue<BigDecimal>();
for (BigDecimal dec : arr) {
_values.add(dec);
}
}
public void put(PointPair item) throws InterruptedException {
while (item.realX.compareTo(_values.peek()) != 0) {
synchronized (this) {
// Block until someone enters the next desired value
wait();
}
}
_list.add(item);
_values.poll();
synchronized (this) {
notifyAll();
}
}
}
}
我的問題是任何人可以幫助我找到的線程錯誤?
謝謝!
如果你知道到底有多少價值爲什麼不分配適當大小的數組,並讓每個線程將值寫入正確的點?沒有阻塞,沒有比賽條件... – tzaman 2010-05-01 00:28:17
啊...嗯,是的,這將是一個更好的解決方案。呃,當你疲倦的時候,你有沒有編碼的原因。感謝您的建議,我會執行它並查看它的執行情況。 – mgbowen 2010-05-01 00:34:22
您的解決方案做得很好,而且性能更快!非常感謝你! – mgbowen 2010-05-01 00:41:22