如何在java中终止匿名线程

如何在java中终止匿名线程,java,multithreading,audio,concurrency,Java,Multithreading,Audio,Concurrency,这个战舰游戏是我尝试编写的第一个多线程应用程序,在我添加多线程之前,它工作得非常好,而多线程只用于声音效果。它是在一个单一的类,音频管理器 我很确定我只是缺乏关于并发性的经验和/或理解,尽管我已经阅读了java教程等。我想我只是需要一些帮助来让它点击 无论如何,游戏运行正常,直到播放了足够多的声音,导致内存不足,并出现以下错误: Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: unable to create

这个战舰游戏是我尝试编写的第一个多线程应用程序,在我添加多线程之前,它工作得非常好,而多线程只用于声音效果。它是在一个单一的类,音频管理器

我很确定我只是缺乏关于并发性的经验和/或理解,尽管我已经阅读了java教程等。我想我只是需要一些帮助来让它点击

无论如何,游戏运行正常,直到播放了足够多的声音,导致内存不足,并出现以下错误:

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: unable to create new native thread
我为每个声音效果创建了一个新的线程,因为我不希望gui等待声音结束,因为声音通常彼此非常接近,如果它们重叠,我不希望它们在同一线程上冲突。我认为问题在于,我不确定在播放声音后如何关闭每个线程而不使主线程暂停

这是一个包含所有声音代码的类。使用setSound()方法播放声音,该方法设置要播放的声音,然后使用Runnable的SoundPlayer内部类启动一个新线程。提前感谢:

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.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class AudioManager {

    private static Thread backgroundThread = new Thread();
    private static int loopCounter = 2;
    private static Clip clip;
    private static String[] backgroundFiles = {
        "/40_Avalon.wav","/13_Glatisant.wav",
        "/31_Lying_In_Deceit.wav","/43_Return_to_Base.wav"};
    private static String[] files = {
        "/bigboom.wav","/Robot_blip.wav",
        "/battleStations.WAV","/beep1.wav",
        "/button-47.wav","/button-35.wav",
        "/beep-23.wav","/Sonar_pings.wav",
        "/button-21.wav","/SONAR.WAV"};
    private static AudioInputStream currentBackgroundMusic;
    private static AudioInputStream currentSound;
    private static boolean backgroundOn = false;
    private static boolean canStart = true;


    private static AudioInputStream loadSound(int s){

        AudioInputStream stream = null;

        try {
            stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(files[s]));
        } catch (UnsupportedAudioFileException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stream;

    }

    private static AudioInputStream loadBackground(int s){

        AudioInputStream stream = null;

        try {
            stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(backgroundFiles[s]));
        } catch (UnsupportedAudioFileException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stream;

    }


    public static void setSound(int s){
        currentSound = loadSound(s);
        Thread thread = new Thread(new SoundPlayer());
        thread.start();
    }

    private static void continueMusic(){
        setBackgroundMusic(loopCounter);
        loopCounter++;
        if(loopCounter > 3) loopCounter = 0;

    }




    public static void playSound(){
        try {
            clip = AudioSystem.getClip();
            clip.open(currentSound);
        } catch (LineUnavailableException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        clip.start();
    }


    public static void setBackgroundMusic(int s){
        if (backgroundOn) {
            backgroundOn = false;
            canStart = false;

            try {
                backgroundThread.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        currentBackgroundMusic = loadBackground(s);
        backgroundThread = new Thread(new MusicPlayer());
        backgroundOn = true;
        backgroundThread.start();
        canStart = true;
    }

    private static void playSound2(AudioInputStream audio) {

        AudioFormat     audioFormat = audio.getFormat();
        SourceDataLine  line = null;
        DataLine.Info   info = new DataLine.Info(SourceDataLine.class,audioFormat);

        try{
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        line.start();

        int     nBytesRead = 0;
        byte[]  abData = new byte[128000];

        while (nBytesRead != -1 && backgroundOn)
        {
            try{
                nBytesRead = audio.read(abData, 0, abData.length);
            } catch (IOException e){
                e.printStackTrace();
            }

            if (nBytesRead == -1) break;

            line.write(abData, 0, nBytesRead);

        }

        line.drain();
        line.stop();
        line.close();
        line = null;
        backgroundOn = false;
    }

    private static class MusicPlayer implements Runnable{

        @Override
        public void run() {
            playSound2(currentBackgroundMusic); 
        }
    }

    private static class SoundPlayer implements Runnable{

        @Override
        public void run() {
            playSound();

        }
    }

    public static void loopMusic(){
        Thread loop = new Thread(new Runnable(){

            @Override
            public void run() {
                while(true){
                    if(backgroundThread.isAlive()){
                        try {
                            backgroundThread.join(0);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else if (canStart){
                        continueMusic();
                    }
                }

            }});

        loop.start();
    }

    public static void reset(){
        loopCounter = 2;
    }


}

正如您所说,您的问题是线程仍在运行或停止,但它们使用的内存没有被java的垃圾收集器(GC)释放

线程在其run()方法返回(完成)或抛出异常时停止。如果发生这种情况,并且代码中的任何地方都没有对该线程的引用,那么它最终将被GC删除(例如,如果您的程序需要一些内存)

因此,首先,检查您创建的线程是否处于一个令人讨厌的无限循环中,如下所示:

public void run() {
    while(true){
        //...
        //Some code
        //...
    }
}
一旦您确定它们在播放声音后正确结束,请确保它们没有在代码中的任何其他地方被引用(程序的一部分仍然保持指向该对象的指针)


最后一个音符。考虑检查这种情况。我更喜欢它,而不是为每个新任务创建一个新线程。它可以减少混乱,提高效率。

您需要去掉所有这些静态变量。这段代码不是线程安全的,因为您实际上试图将所有静态变量用作共享状态

我建议您开始在可运行对象周围传递状态。如果它们需要相互通信,请使用Java附带的内置并发实用程序。如果这还不够,请使用同步和变异对象状态

粗略地看一下您的代码后,我可以看出您的代码将遇到内存可见性问题。您可以尝试通过使静态变量易变来解决这个问题,但我怀疑这是否足够。如果将状态封装在单个对象中,代码将变得更加清晰


在你做任何进一步的工作之前,我会退一步,试着花10-15分钟来提出一个总体设计。您将拥有什么对象,每个对象将拥有什么职责,它们将处于什么状态(不可变/可变)。这些对象如何相互同步(如果它们在不同的线程中运行?

您的代码不是线程安全的

等等,让我稍后再谈。你的OOME是由创建的线程太多造成的。你当前的并发体验只是阅读在线教程,对吗?这些只是介绍如何同步和共享,而不是线程如何工作

线程有很大的设置和拆卸开销,您所做的工作称为无限线程创建,这意味着您只需创建越来越多的线程,直到系统不再支持它为止

首先,您可以使用线程池来解决问题,或者更具体地说,使用
ExecutorService
来并发执行任务。缓存线程池就是您所需要的

其次,您有大量的共享状态字段。您可以通过为原子引用(或类似操作)上的每个操作的单个状态快照创建一个不可变的包装器来解决这个问题,或者您可以同步对字段的访问

第三,请去掉所有的静态字段和方法。虽然没有足够的代码来验证我的问题,但我认为在这种情况下它并不合适。

“Clip.start()方法立即返回,系统在后台线程中播放声音文件。”(从中讨论了如何依次播放声音)

由于您创建的线程有效地运行“make clip object and start it”,它们实际上几乎什么都不做。甚至I/O操作(打开流)都是在正手(在主GUI线程中)完成的


您认为GUI必须等待剪辑完成的假设似乎是无效的。我怀疑如果它们重叠,在同一线程上可能会发生冲突。您能确认GUI对多线程的响应更灵敏吗?我的猜测是,由于创建和启动新线程并不便宜,GUI的响应实际上较差。

首先LH:噢,谢谢大家发表答案。你们都帮了大忙,解决办法就是把你们的答案结合起来。我决定把我自己的答案和我提出的解决办法一起发表出来,以利于其他可能有同样问题的人

事实证明,我确实创建了太多线程,操作系统只允许Java有一定的内存空间,所以我通过使用ExecutorService解决了这个问题

然而,我仍然有同样的问题,即使我没有显式地创建许多新线程。为什么?因为我创建了新的片段来播放声音

我认为剪辑以某种方式创建了播放声音的线程,因此它们可以在不锁定程序或GUI(我以前不了解)的情况下播放。因此,为了一劳永逸地解决这个问题,并允许我的游戏连续快速播放完全相同的声音,而无需剪辑或等待前一个声音播放
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class AudioManager {

    private static ExecutorService backgroundPool = Executors.newFixedThreadPool(1);
    private static Future<?> backgroundStatus;
    private static int loopCounter = 2;
    private static String[] backgroundFiles = {
        "/40_Avalon.wav","/13_Glatisant.wav",
        "/31_Lying_In_Deceit.wav","/43_Return_to_Base.wav"};
    private static String[] files = {
        "/bigboom.wav","/Robot_blip.wav",
        "/battleStations.WAV","/beep1.wav",
        "/button-47.wav","/button-35.wav",
        "/beep-23.wav","/Sonar_pings.wav",
        "/button-21.wav","/SONAR.WAV"};
    private static AudioInputStream currentBackgroundMusic;
    private static boolean backgroundOn = false;
    private static boolean canStart = true;

    private static int[] clipIndex = new int[10];

    private static Clip[][] clips = new Clip[10][10];

    private static void initializeClips(int sound){

        clipIndex[sound] = 0;

        for (int i = 0 ; i < 10 ; i++)
            try {
                clips[sound][i] = AudioSystem.getClip();
                clips[sound][i].open(loadSound(sound));
                clips[sound][i].addLineListener(new LineListener(){

                    @Override
                    public void update(LineEvent event) {
                        if(event.getType() == javax.sound.sampled.LineEvent.Type.STOP){
                            clips[sound][clipIndex[sound]].setFramePosition(0);
                        }
                    }});
            } catch (LineUnavailableException | IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }





    private static AudioInputStream loadSound(int s){

        AudioInputStream stream = null;

        try {
            stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(files[s]));
        } catch (UnsupportedAudioFileException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stream;

    }

    private static AudioInputStream loadBackground(int s){

        AudioInputStream stream = null;

        try {
            stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(backgroundFiles[s]));
        } catch (UnsupportedAudioFileException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stream;

    }


    public static void setSound(int s){
        if(clips[s] == null){
            initializeClips(s);
        }
        playSound(s);
    }

    private static void continueMusic(){
        setBackgroundMusic(loopCounter);
        loopCounter++;
        if(loopCounter > 3) loopCounter = 0;
    }

    public static void playSound(int sound){
        if(clips[sound][0] == null){
            initializeClips(sound);
        }
        clips[sound][clipIndex[sound]].start();
        clipIndex[sound]++;
        if(clipIndex[sound] == 10){
            clipIndex[sound] = 0;
        }
        clips[sound][clipIndex[sound]].drain();
        clips[sound][clipIndex[sound]].flush();
        clips[sound][clipIndex[sound]].setFramePosition(0);

    }


    public static void setBackgroundMusic(int s){

        canStart = false;

        if (backgroundOn) {
            backgroundOn = false;
        }
        currentBackgroundMusic = loadBackground(s);
        backgroundStatus = backgroundPool.submit(new MusicPlayer());
        canStart = true;
    }

    private static void playSound2(AudioInputStream audio) {

        backgroundOn = true;
        AudioFormat     audioFormat = audio.getFormat();
        SourceDataLine  line = null;
        DataLine.Info   info = new DataLine.Info(SourceDataLine.class,audioFormat);

        try{
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(audioFormat);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        line.start();

        int     nBytesRead = 0;
        byte[]  abData = new byte[128000];

        while (nBytesRead != -1 && backgroundOn){
            try{
                nBytesRead = audio.read(abData, 0, abData.length);
            } catch (IOException e){
                e.printStackTrace();
            }

            if (nBytesRead == -1) break;

            line.write(abData, 0, nBytesRead);

        }

        line.drain();
        line.stop();
        line.close();
        line = null;
        backgroundOn = false;
    }

    private static class MusicPlayer implements Runnable{

        @Override
        public void run() {
            playSound2(currentBackgroundMusic); 
        }
    }

    public static void loopMusic(){

        Thread loop = new Thread(new Runnable(){

            @Override
            public void run() {
                while(true){
                    if(backgroundStatus.isDone() && canStart){
                        continueMusic();
                    }
                }

            }});

        loop.start();
    }
    public static void reset(){
        loopCounter = 2;
    }


}
public class Test1 {

    static long count = 0L;

    public static void main(String[] args) {
        while(true){
            count ++;
            System.out.println(count);
            new Thread(new Runnable(){

                @Override
                public void run() {
                    try {
                        Thread.sleep(60000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                }}).start();
        }

    }

}
import java.io.IOException;
import java.util.ArrayList;

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;

import audio.AudioManager;

public class Test2 {

    static long count = 0L;
    static ArrayList<Clip> clips = new ArrayList<>();

    public static void main(String[] args) {
        while(true){
            count ++;
            System.out.println(count);
            try {
                Clip clip1 = AudioSystem.getClip();
                clip1.open(loadSound());
                clips.add(clip1);

            } catch (LineUnavailableException | IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    public static AudioInputStream loadSound(){

        AudioInputStream stream = null;

        try {
            stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource("/bigboom.wav"));
        } catch (UnsupportedAudioFileException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return stream;

    }

}