2015-04-29 68 views
3

您好我有繪製圖像的繪製方法,我有另一種方法是不斷修改要繪製的圖像,但是我一次又一次遇到併發異常。什麼是解決這個問題的最有效的方法?我知道我可以在緩存的圖像上使用同步塊,但是它會引發警告,同步沒有最終變量。Java併發異常

private BufferedImage img; 

public void modImage(BufferedImage image) { 
    img = image; 
} 

public void paintComponent(Graphics g) { 
    if (img != null) { 
     g.drawImage(img, 0, 0, this); 
    } 
} 
+0

您可以在兩個不同的緩衝區之間切換。一個用於渲染,一個用於編輯。 – Harvtronix

+0

對不起,你可以舉一個例子來說明如何使用緩衝區來達到這個目的。 –

+0

在非最終變量上同步沒有任何內在的錯誤。您可能會將您的警告級別設置得過高。 – immibis

回答

1

你有一個競爭條件,因爲img的值可以在if條件和drawImage

if (img != null) {      // <-- img can be non-null here 
    g.drawImage(img, 0, 0, this);  // <-- img can now be null 
} 

使用img您可以指定img到一個局部變量之間的另一個線程改變,那麼在上面的代碼中使用本地變量img。即使img更改爲一個不同的對象在另一個線程

final BufferedImage localImg = img;  // <-- localImg won't change locally 
if (localImg != null) { 
    g.drawImage(localImg, 0, 0, this); 
} 

除此之外,img應該聲明爲volatile因此其價值沒有被緩存線程局部的局部變量將保持不變;其他線程中的更改將被其他人看到。

private volatile BufferedImage img; 

記住,變量聲明爲volatile將導致同步發生每當變量被訪問;所以同步仍然是。但是,同步發生在img引用本身上,而不是BufferedImage引用的對象,因此如果imgnull,則此處沒有問題。

+1

如果沒有同步,則不能保證線程讀取'img'將看到其他線程的寫入。 – user2357112

+0

@ user2357112好點! 'img'需要被聲明爲volatile。我會更新我的答案。 – pathfinderelite

1

,你可以在實例同步,

private BufferedImage img; 

public void modImage(BufferedImage image) { 
    synchronized(this){ 
     img = image; 
    } 
} 

public void paintComponent(Graphics g) { 
    synchronized(this){ 
    if (img != null) { 
     g.drawImage(img, 0, 0, this); 
     } 
    } 
} 
0

有一個線程安全問題,因爲BufferedImage的內容能夠paintComponent方法運行時進行更改。 ui類中的同步和易失性不會解決這個問題。

如果你想添加同步,那麼你需要確保圖像的任何修改都是同步的。這需要在涉及的每個類之間共享的某個對象上同步。

您可以通過確保將img設置爲每次BufferedImage或不同實例的副本來避免某些同步。它應該仍然是不穩定的,並且如果你想安全地將img設置回null,請檢查pathfinderelite的答案。