线程继续在run()方法之后运行

时间:2013-05-26 18:28:06

标签: java eclipse multithreading audio

我在游戏中播放声音时遇到问题。当处理声音播放的线程退出它的run方法时,它不会终止/结束/停止。我知道这是导致问题的方法,因为当我评论整个事情时,不再创建Threads。 (使用JVisualVM检查)。问题是退出run方法后线程不会被终止。我已经放置了一个print命令来确保它实际到达run()方法的末尾,而且它总是这样做。

但是,当我使用JVisualVM检查进程时,对于播放的每个声音,线程数增加1。我还注意到,对于每个播放的声音,守护程序线程的数量增加了1。我不确定守护程序的线程是什么以及它们是如何工作的,但我试图以多种方式杀死线程。包括Thread.currentThread .stop() .destroy() .suspend() .interrupt()并返回run()方法返回;

在写这条消息时,我意识到我需要关闭剪辑对象。这导致没有创建和维持额外的线程。然而,现在声音有时会消失,我不知道为什么。现在,我可以选择并行发声并看到我的cpu被无数个线程超载,或者每当播放新声音时声音都会突然结束。

如果有人知道并行播放多个声音的不同方法或知道我的代码有什么问题,我将非常感谢任何帮助。

以下是方法:

public static synchronized void playSound(final String folder, final String name) {
        new Thread(new Runnable() { // the wrapper thread is unnecessary, unless it blocks on the Clip finishing, see comments
            @Override
            public void run() {
                Clip clip = null;
                AudioInputStream inputStream = null;
                try{
                    do{
                        if(clip == null || inputStream == null)
                                clip = AudioSystem.getClip();
                                inputStream = AudioSystem.getAudioInputStream(SoundP.class.getResource(folder + "/" + name));
                        if(clip != null && !clip.isActive())
                                inputStream = AudioSystem.getAudioInputStream(SoundP.class.getResource(folder + "/" + name));
                                clip.open(inputStream);
                                clip.start(); 
                    }while(clip.isActive());
                    inputStream.close();
                } catch (LineUnavailableException e) {
                    e.printStackTrace();
                } catch (UnsupportedAudioFileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

2 个答案:

答案 0 :(得分:9)

Java Threads的一些内容:

关于您的代码的一些事项:

  • 许多用户提到你缺少大括号
  • 我不太明白你想要实现的目标,但do-while循环似乎是多余的。还有其他好方法可以等待声音完成播放(如果这是你的目标),并且循环不是其中之一。 while循环运行多次而没有Sleep,无缘无故地占用CPU。
  • 如上所述,您应该Close() Stop()Clip,以释放系统资源。

带有调试说明的工作示例:

尝试运行此代码,看看它是否符合您的要求。我已经添加了一些线程方法调用和一些System.out.print用于查看每个代码发生的时间。尝试使用tryToInterruptSoundmainTimeOut来查看它对输出的影响。

import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class PlaySound {
    private static boolean tryToInterruptSound = false;
    private static long mainTimeOut = 3000;
    private static long startTime = System.currentTimeMillis();

    public static synchronized Thread playSound(final File file) {

        Thread soundThread = new Thread() {
            @Override
            public void run() {
                try{
                    Clip clip = null;
                    AudioInputStream inputStream = null;
                    clip = AudioSystem.getClip();
                    inputStream = AudioSystem.getAudioInputStream(file);
                    AudioFormat format = inputStream.getFormat();
                    long audioFileLength = file.length();
                    int frameSize = format.getFrameSize();
                    float frameRate = format.getFrameRate();
                    long durationInMiliSeconds = 
                            (long) (((float)audioFileLength / (frameSize * frameRate)) * 1000);

                    clip.open(inputStream);
                    clip.start();
                    System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound started playing!");
                    Thread.sleep(durationInMiliSeconds);
                    while (true) {
                        if (!clip.isActive()) {
                            System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound got to it's end!");
                            break;
                        }
                        long fPos = (long)(clip.getMicrosecondPosition() / 1000);
                        long left = durationInMiliSeconds - fPos;
                        System.out.println("" + (System.currentTimeMillis() - startTime) + ": time left: " + left);
                        if (left > 0) Thread.sleep(left);
                    }
                    clip.stop();  
                    System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound stoped");
                    clip.close();
                    inputStream.close();
                } catch (LineUnavailableException e) {
                    e.printStackTrace();
                } catch (UnsupportedAudioFileException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    System.out.println("" + (System.currentTimeMillis() - startTime) + ": sound interrupted while playing.");
                }
            }
        };
        soundThread.setDaemon(true);
        soundThread.start();
        return soundThread;
    }

    public static void main(String[] args) {
        Thread soundThread = playSound(new File("C:\\Booboo.wav"));
        System.out.println("" + (System.currentTimeMillis() - startTime) + ": playSound returned, keep running the code");
        try {   
            Thread.sleep(mainTimeOut );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (tryToInterruptSound) {
            try {   
                soundThread.interrupt();
                Thread.sleep(1); 
                // Sleep in order to let the interruption handling end before
                // exiting the program (else the interruption could be handled
                // after the main thread ends!).
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("" + (System.currentTimeMillis() - startTime) + ": End of main thread; exiting program " + 
                (soundThread.isAlive() ? "killing the sound deamon thread" : ""));
    }
}
  • playSound在守护程序线程上运行,因此当主(并且只有非守护程序)线程结束时,它会停止。
  • 我根据this guy计算了声音文件的长度,因此我知道在高级时间内继续播放Clip的时间。这样我可以让Thread Sleep()并且不使用CPU。我使用额外的isActive()作为测试来查看它是否真的结束了,如果没有 - 再次计算剩余时间和Sleep()(声音可能仍会在第一个Sleep之后播放由于两个事实:1。长度计算不需要考虑微秒,以及2. "you cannot assume that invoking sleep will suspend the thread for precisely the time period specified")。

答案 1 :(得分:1)

您的代码实际上就是这个

public static synchronized void playSound(final String folder, final String name) {
    new Thread(new Runnable() { // the wrapper thread is unnecessary, unless it blocks on the Clip finishing, see comments
        @Override
        public void run() {
            Clip clip = null;
            AudioInputStream inputStream = null;
            try{
                do{
                    if(clip == null || inputStream == null){
                            clip = AudioSystem.getClip();
                    }
                    inputStream = AudioSystem.getAudioInputStream(SoundP.class.getResource(folder + "/" + name));
                    if(clip != null && !clip.isActive()){
                            inputStream = AudioSystem.getAudioInputStream(SoundP.class.getResource(folder + "/" + name));
                    }
                    clip.open(inputStream);
                    clip.start(); 
                }while(clip.isActive());
                inputStream.close();
            } catch (LineUnavailableException e) {
                e.printStackTrace();
            } catch (UnsupportedAudioFileException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

if语句仅适用于它们之后的第一个命令。第二个'if'是无意义的,因为语句已经运行了。它看起来像每次你循环播放剪辑是'.start'再次产生另一个线程,无论剪辑是否有效。