Цикл while не работает, когда я удаляю System.out.println

Так что этот цикл while практически ничего не делает, пока я не изменю значение bgmPlaying. Работает нормально. Однако, если я удаляю части, в которых написано // тестирование над ним (без разрывов строк), это не сработает.

Этот блок кода на самом деле постоянно проверяет, включена ли музыка.

Любая идея, почему он перестает работать, когда я удаляю части 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();
        }
    });

1 ответ

С помощью volatile а также Thread.sleep действительно обойти проблему, хотя она должна работать. Но большая проблема заключается в том, что код занят, ожидая изменения состояния переменной и в зависимости от общего состояния между двумя потоками. С volatile или же synchronized Вы можете справиться с этими проблемами, но есть лучший способ.

в java.util.concurrent В комплекте есть мощные инструменты для борьбы с параллелизмом. Потоки, volatile, sleep (а также wait и notify) для сравнения похожи на примитивные ручные инструменты. Один из электроинструментов - это BlockingQueue и его различные реализации, которые позволили бы вам реализовать что-то вроде Actor Model в Java.

В модели актера актеры посылают сообщения друг другу, чтобы действовать, но они никогда не разделяют память. Вы можете определить несколько простых сообщений, таких как 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 class, будет выполняться в управляющем потоке, который его вызывает, но это нормально. Поскольку очередь предназначена для параллелизма, команды, поставленные в очередь в потоке управления, будут немедленно видны в потоке проигрывателя. Нет необходимости Thread.sleep,
  • Я добавил константы для двух разных уровней усиления.
  • Я сделал имя аудиофайла параметром командной строки, поскольку у меня нет вашей эпической боевой музыки.
  • Оба AudioPlayer конструктор и тому perform метод выкинет, если вы попытаетесь пройти в null,
Другие вопросы по тегам