我正在尝试按下按钮或从列表中选择播放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上点击
我是线程新手,所以如果这有明显的解决方案我会事先道歉!
答案 0 :(得分:1)
听起来像是你一次性点击多次点击事件而不是一次。一点点日志记录应验证这一点。你的方法对竞争条件很开放。
这两个事件可以如此接近,当一个检查运行它时,看到!running as true。在此之前可以执行running = true,第二个事件也会看到!running as true并输入if子句。然后他们将运行设置为true并生成一个线程来播放mp3。
您需要做的是使方法同步。
public synchronized void playMP3(String name)
http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
如果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.
}
}
输出:
I'm running!
Already running.
I'm running!
自从我使用Swing以来已经多年了,并且忘记了它的事件调度程序是单线程的。所以你的问题比竞争条件更有可能发生。从一开始就把事情写成线程安全仍然没有什么坏处,因为它让你习惯了它并且这样思考。
使用synchronized方法的明确警告......如果只需要对方法的一小部分进行同步,那么性能可能会很糟糕。在这种情况下,您的整个方法需要是线程安全的。
如果只有一小部分需要线程安全,则需要使用synchronized块。
每个实例的线程安全:
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.
}
}
可能比你想要的更多的信息,但我刚看到线程编程的教学次数很多很多次。
答案 1 :(得分:0)
您需要在开始播放mp3之前立即拨打JButton.setEnabled(false);
,然后在播放完mp3后拨打JButton.setEnabled(true);
。
显然,您应该用按钮的对象替换JButton
(例如:playButton.setEnabled()
)。