1

所以这个while循环几乎什么都不做,直到我改变bgmPlaying的值。它工作正常。但是,如果我删除上面写着 //testing 的部分(没有任何换行符),它就不起作用。

这段代码实际上一直在检查音乐是打开还是关闭。

知道为什么当我删除 System.out.println() 部分时它停止工作吗???

这是我的代码:

import java.io.File;

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.FloatControl;

/**
 * This class simply plays a background music in a seperate thread
 * @author Mohammad Nafis
 * @version 1.0
 * @since 04-03-2018
 *
 */
public class AudioPlayer implements Runnable{

    /**
     * this boolean indicates whether the background music is playing
     */
    private boolean bgmPlaying = true; 

    public void stopBGM() {
        bgmPlaying = false;
    }

    public void playBGM() {
        bgmPlaying = true;
    }

    /**
     * this is an overridden method from Runnable interface that executes when a thread starts
     */
    @Override
    public void run() {

        try {
            File soundFile = new File("sounds/epic_battle_music.wav");
            AudioInputStream ais = AudioSystem.getAudioInputStream(soundFile);
            AudioFormat format = ais.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            Clip clip = (Clip)AudioSystem.getLine(info);
            clip.open(ais);
            clip.loop(Clip.LOOP_CONTINUOUSLY);

            //controlling the volume
            FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
            gainControl.setValue(-20);
                clip.start();




                while(true) {

                    if(bgmPlaying) {
                        gainControl.setValue(-20);
                    } else {
                        gainControl.setValue(-80);
                    }

                    while(bgmPlaying) {

                        //testing
                        System.out.println("BGM is on: ");


                        if(bgmPlaying == false) {
                            gainControl.setValue(-80);


                            break;
                        }
                    }

                    while(!bgmPlaying) {

                        //testing
                        System.out.println("BGM is off: ");

                        if(bgmPlaying == true) {
                            gainControl.setValue(-20);


                            break;
                        }
                    }
                }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

这段代码在我的 Controller 类中,它调用了 stop 和 play 方法。

//adding action listener
    window.getpausebutton().addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ev) {   
            new Thread(new SoundEffect("sounds/clickSound.wav")).start();
            bgm.stopBGM();
        }
    });
    window.getplaybutton().addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ev) {
            new Thread(new SoundEffect("sounds/clickSound.wav")).start();
            bgm.playBGM();
        }
    });
4

1 回答 1

1

使用volatileandThread.sleep确实可以解决该问题,尽管它应该可以工作。但更大的问题是代码正忙于等待变量的状态改变,并且取决于两个线程之间的共享状态。使用volatileorsynchronized您可以管理这些问题,但有更好的方法。

java.util.concurrent包中有用于处理并发的电动工具。相比之下,线程、易失性、睡眠(以及等待和通知)就像原始的手动工具。其中一个强大的工具是它及其各种实现,它可以让您在 Java 中BlockingQueue实现类似于Actor 模型的东西。

在 Actor 模型中,actor 相互发送消息以执行操作,但它们从不共享内存。您可以定义一些简单的消息,例如 PLAY、MUTE 和 STOP,然后将这些消息从您的控制线程发送到您的播放器线程。如果你使用 BlockingQueue,玩家看到消息不会有问题,也不必忙于等待消息到达。它可以简单地尝试take从队列中获取消息,如果没有消息在等待,它会阻塞直到有消息可用。

以下是如何在代码中实现它:

public enum Command {
    PLAY, STOP, MUTE;
}

import java.io.File;

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.FloatControl;

import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * This class simply plays a background music in a separate thread
 *
 * @author Mohammad Nafis
 * @version 1.0
 * @since 04-03-2018
 */
public class AudioPlayer implements Runnable {
    private final String filename;
    private final BlockingQueue<Command> commandQueue =
            new LinkedBlockingQueue<>();

    public final static int LOUD = -20;
    public final static int QUIET = -80;

    public AudioPlayer(String filename) {
        this.filename = Objects.requireNonNull(filename);
    }

    public void perform(Command command) throws InterruptedException {
        commandQueue.put(Objects.requireNonNull(command));
    }

    @Override
    public void run() {
        try {
            File soundFile = new File(filename);
            AudioInputStream ais = AudioSystem.getAudioInputStream(soundFile);
            AudioFormat format = ais.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            Clip clip = (Clip)AudioSystem.getLine(info);
            clip.open(ais);
            clip.loop(Clip.LOOP_CONTINUOUSLY);

            //controlling the volume
            FloatControl gainControl = (FloatControl)
                    clip.getControl(FloatControl.Type.MASTER_GAIN);
            gainControl.setValue(LOUD);
            clip.start();

            forever: while (true) {
                switch (commandQueue.take()) {
                    case PLAY:
                        gainControl.setValue(LOUD);
                        break;
                    case MUTE:
                        gainControl.setValue(QUIET);
                        break;
                    case STOP:
                        break forever;
                }
            }

            clip.stop();
            clip.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        AudioPlayer player = new AudioPlayer(args[0]);
        Thread thread = new Thread(player);
        thread.start();

        Scanner in = new Scanner(System.in);
        String cmd = "";
        System.out.println("Type mute or play. Or stop to exit.");
        do {
            System.out.print(": ");
            System.out.flush();
            cmd = in.nextLine();
            if ("play".equals(cmd)) player.perform(Command.PLAY);
            else if ("mute".equals(cmd)) player.perform(Command.MUTE);
            else if ("stop".equals(cmd)) player.perform(Command.STOP);
            else System.out.println("I didn't understand that, sorry.");
        } while (!"stop".equals(cmd));

        player.perform(Command.STOP);
        thread.join();
        System.out.println("Be seeing you.");
    }
}

几点注意事项:

  • 我在播放器停止后添加了调用,clip.stop()以便clip.close()音频系统不会保持后台线程运行,从而阻止程序退出。
  • perform(Command)方法虽然在AudioPlayer类中,但将在调用它的控制线程上执行,但这没关系。因为队列是为并发设计的,所以在控制线程上排队的命令将立即在播放器线程上可见。不需要Thread.sleep
  • 我为两个不同的增益水平添加了常数。
  • 我将音频文件的名称设为命令行参数,因为我没有你史诗般的战斗音乐。
  • 如果您尝试AudioPlayer传入.performnull
于 2018-04-18T04:54:48.993 回答