Цикл 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
,