1

我正在創建一個遊戲,可以從文件加載關卡。這是在一個單獨的線程上完成的,而其他一切都是在Event Dispatch Thread上完成的。爲什麼我的代碼不是線程安全的?

我通過從一個非常大的測試文件加載來測試代碼,事實證明事件調度線程有時在加載關卡時沒有響應。

我似乎無法找出原因。下面是我的一些代碼:

public class LevelSelectionWrapper extends GamePanel { 
    ... 
    private JList list; 
    private File[] files; 
    ... 
    //Lock object for synchronization 
    private Object lock = new Object(); 
    //Runnable for loading levels from files on a separate thread 
    private Runnable loader = new Runnable() { 
     @Override 
     public void run() { 
      synchronized(lock) { 
       //Load levels from files 
       List<Level> levels = LevelLoader.load(files); // <------------- 
       ... 
       SwingUtilities.invokeLater(new ListUpdater()); 
      } 
     } 
    }; 
    ... 
    private void createOpenFileButton(Container container) { 
     final JFileChooser fc = ... 
     ... 
     //Create open button 
     JButton openButton = new JButton("Open file"); 
     openButton.setFocusable(false); 
     openButton.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       int returnVal = fc.showOpenDialog(LevelSelectionWrapper.this); 
       if(returnVal == JFileChooser.APPROVE_OPTION) { 
        synchronized(lock) { files = fc.getSelectedFiles(); } 
        //Load files on separate thread 
        new Thread(loader).start(); // <------------- 
       } 
      } 
     }); 
     container.add(openButton); 
    } 
} 

我已經添加了兩個箭頭代碼:

  • 第一個是耗時的方法(當文件確實是很大)。有時,此代碼正在運行時,事件調度線程無響應。
  • 最後一個是調用runnable的地方。
+1

在這種情況下'syncronized(lock)'是什麼?這似乎是一個壞/不必要的同步對我來說。比如,更好的辦法是在本地獲取文件列表,構建副本並將副本傳遞給加載程序,而不是使用必須鎖定的共享對象。如果我誤解了它的目的,或許你可以詳細闡述它。 – dimo414 2013-03-24 21:51:01

+0

僅僅是這個應用程序中的EDT沒有反應,或者系統的其他部分呢? – 2013-03-24 21:51:11

+0

@Ralf:我只使用兩個線程,EDT和工作線程。在工作線程完成之前,EDT沒有響應。 – 2013-03-24 21:53:22

回答

3

我肯定會建議擺脫那個lock對象(和相關的依賴關係)。獲取actionPerformed()中的文件列表並構建副本以傳遞給您的可運行。避免像目前一樣使用像files這樣的實例變量,因爲這些變量是不必要地跨線程共享的。

這些​​塊是我最可能的罪魁禍首。如果這樣不能解決您的問題,我會建議在您認爲阻止的地區周圍添加一些System.out.println()電話,以便確切瞭解哪些電話需要這麼長時間。

還可以考慮使用SwingWorkers而不是自己構建新的線程。這可以爲您節省幾個EDT的線程啓動時間週期。

+1

謝謝,這個答案非常有幫助!我決定使用'SwingWorkers',到目前爲止,Event Dispatch Thread還沒有變得沒有反應。另外,由於鎖定消失了,我現在可以加載其他級別,同時已經加載了級別。 – 2013-03-25 00:00:53

+0

不錯! 'synchronized'塊是很有用的工具,但是通常有許多更乾淨更優雅的解決方案來處理併發。謹慎地使用它們,並且儘可能將數據限制在一個線程中。 – dimo414 2013-03-25 02:06:32

相關問題