2012-03-11 42 views
3

我有一個工作的Java程序,一個簡單的MP3播放器。IllegalThreadStateException?不熟悉線程

一切正常,可以跳過曲目等......但經過幾次跳過(特別是後退按鈕(上一曲)),我總是得到一個IllegalThreadStateException。我不熟悉線程,所以我不確定是什麼做的事。

這是學校裏的功課,和MP3類已經給我們。(不能修改)

任何提示將不勝感激!謝謝!

MP3類(不能被修改):

import java.io.BufferedInputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import javazoom.jl.player.Player; 


public class MP3 extends Thread { 
    private final File mp3_file; 
    private Player player; 

    public MP3(String mp3_path) { 
    mp3_file = new File(mp3_path); 
} 

    public MP3(File mp3) { 
    mp3_file = mp3; 
} 

    public void play() { 
    try { 
     FileInputStream fis  = new FileInputStream(mp3_file); 
     BufferedInputStream bis = new BufferedInputStream(fis); 
     player = new Player(bis); 
    } 
    catch (Exception e) { 
     System.out.println("Problem playing file " + mp3_file); 
     System.out.println(e); 
    } 

    // run in new thread to play in background 
    start(); // Instructs JVM to call run() in separate thread 

    } 

    public boolean isPlaying() { 
    if (player == null) 
     return false; 
    else { 
     return !player.isComplete(); 
    } 

    } 

    public void run() { 
    try { player.play(); } 
    catch (Exception e) { System.out.println(e); } 
    } 


     public void quit() { 
    if (player != null) { 
     player.close(); 
     player = null; 
    } 
    } 

    public String toString() { 
    return mp3_file.toString(); 
    } 
} 

我的代碼:

import javax.swing.*; 

public class MP3Random extends JFrame { 

private JPanel backgroundPanel; 
private PlaylistPanel playlistPanel; 
private MainPanel mainPanel; 

int trackTime; 
private MP3 current; 

private ButtonListener buttonListener; 
private TimerListener timerListener; 

private Timer timer; 
private boolean playButtonStatus; 

public MP3Random() { 

    buttonListener = new ButtonListener(); 
    timerListener = new TimerListener(); 

    backgroundPanel = new JPanel(); 
    mainPanel = new MainPanel(); 
    playlistPanel = new PlaylistPanel(); 

    timer = new Timer(1000, timerListener); 
    timer.setInitialDelay(0); 
    playButtonStatus = false; 

    setMinimumSize(new Dimension(650, 400)); 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    getContentPane().setLayout(new BorderLayout(0, 0)); 

    backgroundPanel.setLayout(new BorderLayout(0, 0)); 
    backgroundPanel.setBorder(null); 
    backgroundPanel.add(playlistPanel, BorderLayout.CENTER); 
    backgroundPanel.add(mainPanel, BorderLayout.NORTH); 

    getContentPane().add(backgroundPanel); 
    setVisible(true); 
} 

private void playSong(MP3 current) { 
    ImageIcon stopIcon = new ImageIcon(MP3Random.class.getResource("/pa2/icons/stop.png")); 

     playlistPanel.playList.setSelectedIndex(playlistPanel.listModel.indexOf(current)); 
    mainPanel.playButton.setIcon(stopIcon); 
    trackTime = 0; 
    current.play(); 
    timer.restart(); 
    mainPanel.trackTitleLabel.setText(getTrackTitle(current.toString())); 
} 

private void stopPlayback() { 
    try { 
     current.quit(); 
     timer.stop(); 
     playButtonStatus = false; 
     mainPanel.trackTitleLabel.setText(""); 
     mainPanel.trackTimeLabel.setText(""); 
     mainPanel.playButton.setIcon(mainPanel.playIcon); 
    } 
    catch (Exception e) {}; 
} 

private String getTrackTitle (String filename) { 
    return filename.substring(filename.lastIndexOf('/')+1, filename.lastIndexOf(".mp3")); 
} 

private String formatTime(int durationInSeconds) { 

    int minutes = durationInSeconds/60; 
    int seconds = durationInSeconds % 60; 

    return ((minutes < 10 ? "0" : "") + minutes 
      + ":" + (seconds< 10 ? "0" : "") + seconds); 
} 

//Panel containing track display/time, media control buttons 
private class MainPanel extends JPanel {...} 

//Panel containing JList and add/remove from playlist buttons 
private class PlaylistPanel extends JPanel {...} 

private class ButtonListener implements ActionListener { 

    File[] filesSelected; 
    MP3[] mp3List; 
    Random generator = new Random(); 

    public void actionPerformed (ActionEvent event) { 

     if (event.getSource() == playlistPanel.addButton) { 
      playlistPanel.fileChooser = new JFileChooser(); 
      playlistPanel.fileChooser.setMultiSelectionEnabled(true); 

      //FileFilter only allows *.mp3 
      playlistPanel.fileChooser.setAcceptAllFileFilterUsed(false); 
      playlistPanel.fileChooser.setFileFilter(new FileFilter() { 

       public boolean accept(File f) { 
        if (f.isDirectory()) 
         return true; 

        String extension = f.toString().substring(f.toString().lastIndexOf('.')+1); 
        if (extension != null) { 
         if (extension.equals("mp3")) 
          return true; 
         else 
          return false; 
        } 
        return false; 
       } 

       public String getDescription() { 
        return "*.mp3"; 
       } 
      }); 

      if (playlistPanel.fileChooser.showOpenDialog(MP3Random.this) == JFileChooser.APPROVE_OPTION) { 

       filesSelected = playlistPanel.fileChooser.getSelectedFiles(); 
       mp3List = new MP3[filesSelected.length]; 

       mainPanel.playButton.setEnabled(true); 
       playlistPanel.removeButton.setEnabled(true); 

       //more than 1 file selected 
       if (mp3List.length > 1) { 
        mainPanel.shuffleButton.setEnabled(true); 
        mainPanel.nextButton.setEnabled(true); 
       } 
       for (int i = 0; i < mp3List.length; i++) 
        playlistPanel.listModel.addElement(mp3List[i] = new MP3(filesSelected[i])); 
       current = (MP3) playlistPanel.listModel.get(0); 
       playlistPanel.playList.setSelectedIndex(playlistPanel.listModel.indexOf(current)); 
      } 
     } 

     //remove button 
     if (event.getSource() == playlistPanel.removeButton) { 
      Object[] temp = playlistPanel.playList.getSelectedValues(); 
      for (Object f : temp) { 
       playlistPanel.listModel.removeElement(f); 
      } 

      //1 song in list 
      if (playlistPanel.listModel.getSize() == 1) { 
       mainPanel.backButton.setEnabled(false); 
       mainPanel.nextButton.setEnabled(false); 
       mainPanel. shuffleButton.setEnabled(false); 
      } 

      //no songs in list 
      if (playlistPanel.listModel.getSize() == 0) { 
       playlistPanel.removeButton.setEnabled(false); 
       mainPanel.playButton.setEnabled(false);    
       mainPanel.backButton.setEnabled(false); 
       mainPanel.nextButton.setEnabled(false); 
       mainPanel.shuffleButton.setEnabled(false); 
       current = null; 
      } 
     } 

     //play button 
     if (event.getSource() == mainPanel.playButton) { 

      //if song not playing 
      if (!playButtonStatus) { 
       if (!playlistPanel.playList.isSelectedIndex(playlistPanel.listModel.indexOf(current))) 
        current = (MP3) playlistPanel.playList.getSelectedValue(); 
       playSong(current);     
       playButtonStatus = true; 
      } 

      //if song is playing 
      else 
       stopPlayback(); 
     } 

     //next button 
     if (event.getSource() == mainPanel.nextButton) { 
      if (!mainPanel.backButton.isEnabled()) 
       mainPanel.backButton.setEnabled(true); 

      if (current.isPlaying()) { 
       current.quit(); 
       current = (MP3) playlistPanel.listModel.get(playlistPanel.listModel.indexOf(current)+1); 
       playSong(current); 
      } 
      else { 
       current = (MP3) playlistPanel.listModel.get(playlistPanel.listModel.indexOf(current)+1); 
       playlistPanel.playList.setSelectedIndex(playlistPanel.listModel.indexOf(current)); 
      } 

      if (playlistPanel.listModel.indexOf(current) == playlistPanel.listModel.size() - 1) 
       mainPanel.nextButton.setEnabled(false); 
     } 

     //back button 
     if (event.getSource() == mainPanel.backButton) { 

      if (!mainPanel.nextButton.isEnabled()) 
       mainPanel.nextButton.setEnabled(true); 

      if (current.isPlaying()) { 
       current.quit(); 
       current = (MP3) playlistPanel.listModel.get(playlistPanel.listModel.indexOf(current)-1); 
       playSong(current); 
      } 
      else { 
       current = (MP3) playlistPanel.listModel.get(playlistPanel.listModel.indexOf(current)-1); 
       playlistPanel.playList.setSelectedIndex(playlistPanel.listModel.indexOf(current)); 
      } 

      if (playlistPanel.listModel.indexOf(current) == 0) 
       mainPanel.backButton.setEnabled(false); 
     } 

     //shuffle jlist 
     if (event.getSource() == mainPanel.shuffleButton) { 
      if (playlistPanel.listModel.size() > 1) { 
       int n = playlistPanel.listModel.getSize(); 
       while (n > 1) { 
        int k = generator.nextInt(n); 
        n--;     
        MP3 tempMP3 = (MP3) playlistPanel.listModel.elementAt(n); 
        playlistPanel.listModel.set(n,playlistPanel.listModel.elementAt(k)); 
        playlistPanel.listModel.set(k, tempMP3); 
        current = (MP3) playlistPanel.listModel.get(0); 
        playlistPanel.playList.setSelectedIndex(playlistPanel.listModel.indexOf(current)); 
       } 
      } 
     } 
    } 
} 

private class TimerListener implements ActionListener { 

    public void actionPerformed(ActionEvent event) { 

     if (playlistPanel.listModel.indexOf(current) > 0) 
      mainPanel.backButton.setEnabled(true); 
     if (playlistPanel.listModel.indexOf(current) == playlistPanel.listModel.size()-1) 
      mainPanel.nextButton.setEnabled(false); 
     if (!current.isPlaying()) { 
      if (playlistPanel.listModel.size() > 1 && playlistPanel.listModel.indexOf(current) < playlistPanel.listModel.size()-1) { 
       current = (MP3) playlistPanel.listModel.get(playlistPanel.listModel.indexOf(current)+1); 
       playSong(current); 
      } 
      else 
       stopPlayback(); 
     } 
     else { 
      mainPanel.trackTimeLabel.setText(formatTime(trackTime)); 
      trackTime++; 
     } 
    } 
} 

public static void main(String[] args) throws Exception { 

    MP3Random instance = new MP3Random(); 
} 
} 

錯誤:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalThreadStateException 
at java.lang.Thread.start(Thread.java:656) 
at pa2.MP3.play(MP3.java:41) 
at pa2.MP3Random.playSong(MP3Random.java:68) 
at pa2.MP3Random.access$4(MP3Random.java:62) 
at pa2.MP3Random$ButtonListener.actionPerformed(MP3Random.java:373) 
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2028) 
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2351) 
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387) 
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242) 
at  javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236) 
at java.awt.Component.processMouseEvent(Component.java:6373) 
at javax.swing.JComponent.processMouseEvent(JComponent.java:3267) 
at java.awt.Component.processEvent(Component.java:6138) 
at java.awt.Container.processEvent(Container.java:2085) 
at java.awt.Component.dispatchEventImpl(Component.java:4735) 
at java.awt.Container.dispatchEventImpl(Container.java:2143) 
at java.awt.Component.dispatchEvent(Component.java:4565) 
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4621) 
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4282) 
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4212) 
at java.awt.Container.dispatchEventImpl(Container.java:2129) 
at java.awt.window.dispatchEventImpl(window.java:2478) 
at java.awt.Component.dispatchEvent(Component.java:4565) 
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:679) 
at java.awt.EventQueue.access$000(EventQueue.java:85) 
at java.awt.EventQueue$1.run(EventQueue.java:638) 
at java.awt.EventQueue$1.run(EventQueue.java:636) 
at java.security.AccessController.doPrivileged(Native Method) 
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87) 
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98) 
at java.awt.EventQueue$2.run(EventQueue.java:652) 
at java.awt.EventQueue$2.run(EventQueue.java:650) 
at java.security.AccessController.doPrivileged(Native Method) 
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87) 
at java.awt.EventQueue.dispatchEvent(EventQueue.java:649) 
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296) 
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211) 
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201) 
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196) 
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188) 
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122) 

回答

4

首先,你需要熟悉線程,因爲這個項目,你正在努力使用它們!你的課本可能會涵蓋這個主題。仔細閱讀。線程非常重要。如果你的教科書不清楚,你也可以read the official Java tutorial

回到你的問題。您的MP3類的每個實例都在單獨的線程中運行。 MP3線程與主程序執行(表示運行主方法的線程)是分離的,而且擺動UI也在單獨的線程中運行。

你的錯誤指示,引用的Javadoc java.lang.IllegalThreadStateException

a thread is not in an appropriate state for the requested operation. See, for example, the suspend and resume methods in class Thread.

這裏有一些事情看:

  1. 您的計時器類可以停止和啓動MP3線程。它調用playSong(current);和stopPlayback()。還有另一個類也調用這些方法嗎?您的錯誤是由用戶事件觸發的:pa2.MP3Random $ ButtonListener.actionPerformed(MP3Random.java:373) 您是否在線程準備好播放前單擊該按鈕?
  2. 一般來說,您需要確保兩個線程不會相互干擾。一個典型的例子是兩個線程都修改相同的列表。如果第一個線程從列表中刪除一個項目,而第二個線程遍歷列表(使用迭代器或每個循環),則會拋出異常。
  3. 你可以測試出沒有GUI的播放mp3的給定類嗎?只是一個靜態方法調用。你能讓玩家工作嗎?

點數2在我的答案中可能與您的問題無關 - 在使用線程時理解這一點非常重要。例如,如果您的PlaylistPanel中可用歌曲的列表發生更改(多個線程可以更改它),那麼您需要使該類的一部分同步。如果另一個線程正在運行一個同步方法,這將強制一個線程等待。你的問題是試圖重新啓動一個線程,是「死」看JavaDocs for Thread

It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution.

因此,要啓動線程,你叫開始。開始呼叫運行。該線程現在將與其他線程同時「運行」,直到run()終止。一旦它終止,我們就完成了該線程。如果我們想要同一個線程播放多首歌曲,我們需要實施這種行爲。但是你發佈的run方法(我認爲你沒有寫;它是作爲這個任務的一部分給出的)非常簡單。它調用player.play();,當palay結束時,線程完成。

我認爲run()方法的最後一行應該是player = null,這樣isPlaying()方法才能正常工作。

+0

嗨索恩的幫助的感謝! 按下按鈕後,錯誤確實立即彈出(不是在第一個按鈕上按下,而是在一對下一個/後退之後,停止 - 開始點擊) 我理解你的第二點,這很有道理,我不知道如何從代碼深處排除故障。 – Wangagat 2012-03-11 13:43:56

+0

@Wangagat每次歌曲需要播放時,你是否正在創建一個新的MP3實例? – quaylar 2012-03-11 13:57:47

+0

這是一個有趣的任務。它是什麼課程? – Thorn 2012-03-11 15:05:00

2

只是一個猜測,但:是不是,你是從你的線程調用AWT-Widgets的方法?對於SWT,這不起作用,例如,必須從GUI線程(即創建它們的線程)調用widget-calls。 不知道這是AWT一樣的,但它的價值來檢查......

+0

這不太可能。最可能的答案是已經說過的。每次需要播放歌曲時,都必須創建一個新的mp3對象。我無法獲得給定的代碼進行編譯。我們缺少類: MainPanel擴展JPanel和 PlaylistPanel擴展JPanel – Thorn 2012-03-11 14:45:49

+0

@Thorn更正,請參閱我對GETAH答案的評論。 – quaylar 2012-03-11 14:59:28

7

的問題是在這裏:

public void play() { 
    try { 
    FileInputStream fis  = new FileInputStream(mp3_file); 
    BufferedInputStream bis = new BufferedInputStream(fis); 
    player = new Player(bis); 
    } 
    catch (Exception e) { 
    System.out.println("Problem playing file " + mp3_file); 
    System.out.println(e); 
    } 
    // run in new thread to play in background 
    start(); // <======== PROBLEM 
} 

Thread.Startjava doc說:

Throws: IllegalThreadStateException - if the thread was already started.

基本上,線程不能啓動超過一次。只要您撥打Play上的MP3實例已經啓動MP3.Play() 我會建議您創建一個新線程,以在調用MP3.Play時播放您的曲目。 Player類可能看起來像:

public class MP3Player extends Thread { 
    public void PlayFile(String soundFile){ 
     //... Add player logic here 
    } 
    public void StopPlaying(){ 
     //.. Stop playing and gracefully exit this thread 
    } 
} 

而在MP3類:

public class MP3{ 
    MP3Player currentPlayer = null; 
    // ... 
    public void play() { 
     if(currentPlayer != null) currentPlayer.StopPlaying(); 
     currentPlayer = new MP3Player(); 
     currentPlayer.PlayFile(mp3_file); 
    } 
} 
+0

我不認爲這是事實,因爲他將MP3實例傳遞給MP3Random.playSong()方法。如果你的理論成立,他必須每次都傳遞相同的MP3實例...注意,MP3已經是一個線程了! – quaylar 2012-03-11 13:52:48

+0

每當我按下「後退/下一首/播放」按鈕播放以前播放的歌曲時,都會發生錯誤。 由於我不能編輯mp3類,我怎樣才能修改我自己的代碼呢? – Wangagat 2012-03-11 13:57:55

+0

@Wangagat然後GETAH是對的。每次播放歌曲時,都需要創建一個「MP3」類的新實例。你在同一個實例上多次調用'MP3.play()',所以你試圖啓動一個已經啓動的線程。這不起作用。 – quaylar 2012-03-11 13:59:07