3

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

但是,当我使用 来检查进程时JVisualVM,每个播放的声音线程数都会增加11我还注意到,每个播放的声音都会增加守护线程的数量。我不确定守护线程是什么以及它们是如何工作的,但我已经尝试以多种方式杀死线程。通过return包含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();
    }
4

2 回答 2

9

关于 Java 线程的一些事情:

  • 一个线程在退出它的 Run() 方法时总是会死掉。在您的情况下,其他线程是在您调用的方法中创建的,但是您的线程结束(您可以通过命名来检查它并查看它何时死亡)。
  • .stop()永远不要使用,.destroy()或杀死线程.suspend()。这些方法已弃用,不应使用。相反,您基本上应该到达 Run() 方法的末尾。这就是Thread.interrupt()目的,但是您必须通过检查Thread.isInterrupted()标志然后抛出InterruptedException和处理它来支持中断线程(有关更多详细信息,请参阅如何停止线程)。
  • “守护线程是一个线程,当程序完成但线程仍在运行时,它不会阻止 JVM 退出”

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

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

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

尝试运行此代码,看看它是否满足您的要求。我添加了一些线程方法调用和一些System.out.prints 供您查看每一段代码何时发生。尝试使用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 在守护线程上运行,因此当主线程(并且只有非守护线程)结束时,它会停止。
  • 我已经根据这家伙计算了声音文件的长度,以便我提前知道要播放多长时间Clip。这样我可以让Thread Sleep()和不使用CPU。我用一个额外isActive()的作为测试,看看它是否真的结束了,如果没有 - 计算剩余的时间Sleep()(由于两个事实,在第一个之后声音可能仍然会播放Sleep:1.长度计算不需要微秒考虑到,2. “你不能假设调用 sleep 会在指定的时间段内暂停线程”)。
于 2013-07-19T13:54:40.880 回答
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 语句仅适用于它们之后的第一个命令。第二个“如果”是毫无意义的,因为该语句已经运行。在我看来,每次您循环播放剪辑时,无论剪辑是否处于活动状态,剪辑都会再次“启动”并产生另一个线程。

于 2013-07-19T10:11:05.547 回答