2014-10-03 50 views
0

我的一個應用程序通過讀取一個數組列表,描繪對象的屏幕:Java的油漆「口吃」 -list併發

簡單的代碼摘要:

@Override 
public synchronized void paintComponent(Graphics g) { 
    for(Object gO:paintList) { 
    g.drawImage(gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null); 
    } 
} 

問題是我每次添加更多對象用戶點擊鼠標,所以如果用戶點擊的速度夠快,我可以導致程序繪畫造成口吃,因爲它在寫入時不能讀取(arrayList被同步)。開發人員用來處理這種併發問題的常見做法是什麼?

編輯:這裏是調用重繪代碼:

byte ticks = 0; 
    while(true) { 
     currentTime = System.nanoTime(); 
     if(ticks == 25) { 
      drawPanel.repaint(); 
      ticks = 0; 
     } else if (ticks%5 == 0) {//if ticks is a multiple of 5 (5,10,15,...) 
      drawPanel.operations(); 
      ticks++; 
     } else if(ticks < 25) { 
      ticks++; 
     } 
     try { 
      /* 
      Refer to: 'http://stackoverflow.com/questions/1036754/difference-between-wait-and-sleep' 
      on differences between Thread.sleep() and wait() 
      */ 
      wait(1);//old timings: (long)(refreshRate*1000) 
     } catch (InterruptedException ex) { 
      Logger.getLogger(DeathWish.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     //Debugging 
     //System.out.println(ticks); 
     currentTime = System.nanoTime(); 

*,其中操作()計算在「上漆」對象的屬性的變化,去除了符合一定條件的對象,並增加了新的對象油漆名單。從邏輯上來說,添加和書寫應該是分開的? 如果沒有足夠的信息,我可以發佈operations()方法,但我試圖不發佈大量代碼,因此更易於解釋。

+0

一般來說,我有連續繪製屏幕的單個循環,以及處理任何事件的另一個循環(如單擊以添加對象)。這樣,如果事件處理程序被卡住了,paint循環將繼續運行而不會出現口吃。 – Jon 2014-10-03 17:03:38

+2

我真的懷疑用戶可以點擊的速度超過可以獲得的鎖定。鎖定採集約25ns。用戶點擊大約是50ms。 – 2014-10-03 17:09:14

+0

你有什麼異常嗎? – Dimitri 2014-10-03 17:24:30

回答

1

我想你把事情有點不對同步:

  • ArrayList中是不同步
  • 一個synchronized方法意味着只有1同時線程可以訪問的方法來實現的列表,但你的函數裏面的變量根本不同步

你想要的是讓你的列表臨時同步。

你可以做這樣的事情:

@Override 
public void paintComponent(Graphics g) { 
    synchronized(paintList) { 
    for(Object gO:paintList) { 
     g.drawImage(gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null); 
    } 
    } 
} 

,並在代碼中添加的對象喲列表做有點相同。從這裏

編輯:

如果你想刪除添加線程和油漆線程之間的所有併發性問題,這裏是你如何能做到:

中添加圖像的方法:

public synchronized void addImage(...) { 
    Something newImage = ..... 
    List<Something> newPaintList = new ArrayList<>(paintList.size() + 1); 
    newPaintList.addAll(paintList); 
    newPaintList.add(newImage); 
    paintList = newPaintList; 
} 

並在paint方法中,刪除同步部分。

@Override 
public void paintComponent(Graphics g) { 
    for(Object gO:paintList) { 
    g.drawImage(gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null); 
    } 
} 

這樣,讀取和寫入之間就不會有任何併發​​,因爲在paintList上完成的唯一操作是讀取。

addImage應該同步,以避免兩個不同的線程同時添加圖像,這可能會使一個addImage被忽略。

+0

問題在於同步,當用戶在列表中產生更多項目時,繪製線程似乎正在等待。 – 2014-10-04 16:30:40

+0

這個add方法與'CopyOnWriteArrayList'基本相同,每次集合發生變化時都會創建一個新副本,但是感謝具體的代碼。 – 2014-10-06 18:20:41