Java 如何阻止mp3文件一次播放多次?

Java 如何阻止mp3文件一次播放多次?,java,multithreading,mp3,Java,Multithreading,Mp3,我正试图通过按下按钮或从列表中选择播放mp3文件(我已经成功地做到了)。然而,我似乎无法阻止歌曲在同一个按钮上播放多次 我想做的是在一个新线程中播放歌曲,禁用再次播放歌曲,直到线程关闭,然后允许再次播放 我的代码如下: public class SoundFactory { private Player player; private static boolean running = false; private String getFile(String name) { Strin

我正试图通过按下按钮或从列表中选择播放mp3文件(我已经成功地做到了)。然而,我似乎无法阻止歌曲在同一个按钮上播放多次

我想做的是在一个新线程中播放歌曲,禁用再次播放歌曲,直到线程关闭,然后允许再次播放

我的代码如下:

public class SoundFactory {

private Player player;
private static boolean running = false;

private String getFile(String name) {
    String f = "sound" + File.separator + name + ".mp3";
    return f;
}

public void playMP3(String name) {


    if (!running) {
        running = true;

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

        // run in new thread to play in background
        new Thread() {
            public void run() {
                try {
                    player.play();
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        }.start();
        //running = false;
    }
}


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

该文件通过以下方式播放:

    SoundFactory sf = new SoundFactory();
    sf.playMp3("song name");
在JButton上单击


我是线程新手,所以如果这有一个明显的解决方案,我会提前道歉

您需要调用
JButton.setEnabled(false),然后调用
JButton.setEnabled(true)当mp3播放完毕时


显然,您应该用按钮的对象(例如:
playButton.setEnabled()
)替换
JButton

我觉得您一次触发多个单击事件,而不是一个。一点日志记录应该可以验证这一点。你的方法是完全开放的比赛条件

这两个事件可以如此紧密地联系在一起,以至于当一个人检查运行它时,就会看到!像真的一样跑。在那之前一个人可以做running=true,第二个事件也会看到!运行为true并输入if子句。然后它们都将running设置为true,并生成一个线程来播放mp3

您需要做的是使您的方法同步

public synchronized void playMP3(String name)

如果count是SynchronizedCounter的实例,则 同步方法有两种效果:

  • 首先,同一对象上的两个同步方法调用不可能交错。当一个线程正在执行时 一个对象的同步方法,所有其他调用 同一对象块的同步方法(暂停执行) 直到第一个线程处理完对象
  • 其次,当同步方法退出时,它会自动与任何后续方法建立“发生在之前”的关系 为同一对象调用同步方法。这 保证对对象状态的更改对所有人都可见 线程
为了澄清我最后的评论,这里有一个测试程序,显示running=false应该放在哪里

public class Test {

  public static boolean running = false;

  public synchronized void runner() {
    if(!running) {
      running = true;
      System.out.println("I'm running!");
      new Thread() {
        public void run() {
          for(int i=0; i<10000; i++) {} // Waste some time
          running = false; // This is only changed once the thread completes its execution.
        }
      }.start();
    } else {
      System.out.println("Already running.");
    }
  }

  public static void main(String[] args) {
    Test tester = new Test();
    tester.runner();
    tester.runner(); // The loop inside the Thread should still be running so this should fail.
    for(int i=0; i<20000; i++) {} // Waste even more time.
    tester.runner(); // The loop inside the Thread should be done so this will work.
  }
}
我使用Swing已经很多年了,忘记了它的事件分派器是单线程的。因此,你的问题更可能是这种情况,而不是比赛条件。从一开始就开始写线程安全的东西仍然没有什么坏处,因为这会让你习惯它,并以这种方式思考

关于使用同步方法的明确警告。。。如果只需要同步方法的一小部分,则会对性能造成严重影响。在这种情况下,整个方法需要是线程安全的

如果只有一小部分需要线程安全,则需要使用同步块

每个实例的线程安全性:

public class myClass {
   public void myFunc() {
      // bunch of code that doesn't need to be thread safe.
      synchronized(this) {
         // Code that needs to be thread safe per instance
      }
      // More code that doesn't need thread safety.
   }
}
所有实例都是线程安全的

public class myClass {
   static Object lock = new Object();
   public void myFunc() {
      // bunch of code that doesn't need to be thread safe.
      synchronized(lock) {
         // Code that needs to be thread safe across all instances.
      }
      // More code that doesn't need thread safety.
   }
}
静态方法中的线程安全

public class myClass {
   public static void myFunc() {
      // bunch of code that doesn't need to be thread safe.
      synchronized(MyClass.class) {
         // Code that needs to be thread safe.
      }
      // More code that doesn't need thread safety.
   }
}

可能比您想要的信息要多得多,但我刚刚看到线程编程的教学效果太差了很多很多次。

我应该补充一点,如果这能解决您的问题,那么禁用和启用按钮将是不必要的。我想我明白了为什么
同步的
方法在这里很有用,但是
running=false行是以任何方式执行的,所以没有任何东西可以阻止您创建另一个线程?即使使用
synchronized
方法,我仍然可以多次(甚至在几秒钟后)按下按钮,歌曲将同时播放多次!我现在注意到running=false在Thread.run()方法之外。您需要将其移动到run方法中,使其成为线程的一部分。在它所在的位置,线程开始执行自己的事情,并立即在playm3方法中继续执行,即running=false。因此,此时MP3仍在播放,但运行现在又是错误的。感谢您花时间撰写如此清晰和广泛的回复。对我这样的初学者来说真的很有用!:)他很可能有比赛的情况。在这种情况下,更改按钮的启用状态不会起任何作用。明智地使用synchronized是正确的做法。
public class myClass {
   public static void myFunc() {
      // bunch of code that doesn't need to be thread safe.
      synchronized(MyClass.class) {
         // Code that needs to be thread safe.
      }
      // More code that doesn't need thread safety.
   }
}