Я пытаюсь написать алгоритм, который вычисляет громкость входа звуковой карты. Хотя это (более или менее) не проблема, я сталкиваюсь с проблемами во время многозадачности. Кажется, что прерывания, которые я вызываю из другого потока, игнорируются.
Раньше я разрабатывал многопоточные среды и думаю, что знаю подводные камни Thread.isInterrupted() по сравнению с Thread.interrupted() и InterruptedException - вот почему я использую исключительно Thread.isInterrupted(). Редактировать: Также я думаю, что ничего из того, что я здесь использую, не выбрасывает InterruptedException.
Итак, я написал этот класс:
import javax.sound.sampled.*;
public class SoundListener extends Thread {
/**
* RMS refresh rate in Hz (1000ms / RESPONSE_TIME)
*/
private static final int REFRESH_RATE = 40;
private final SoundMeter SOUNDMETER;
private final AudioFormat AUDIO_FORMAT;
private final int BUFFER_SIZE;
private final boolean IS_24_BITS;
private final TargetDataLine TARGET_DATA_LINE;
SoundListener(SoundMeter soundMeter, Mixer mixer, AudioFormat audioFormat) throws LineUnavailableException {
SOUNDMETER = soundMeter;
AUDIO_FORMAT = audioFormat;
BUFFER_SIZE = (int) ((AUDIO_FORMAT.getSampleRate() * AUDIO_FORMAT.getSampleSizeInBits()) / REFRESH_RATE);
IS_24_BITS = AUDIO_FORMAT.getSampleSizeInBits() == 24;
TARGET_DATA_LINE = AudioSystem.getTargetDataLine(AUDIO_FORMAT, mixer.getMixerInfo());
TARGET_DATA_LINE.open(AUDIO_FORMAT);
TARGET_DATA_LINE.start();
setName("SoundListener");
setDaemon(true);
start();
}
@Override
public void run() {
System.out.println("Thread " + getName() + " started!");
while (!isInterrupted()) {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = TARGET_DATA_LINE.read(buffer, 0, BUFFER_SIZE);
if (bytesRead >= 0) {
int max = 0;
for (int i = 0; i < bytesRead; ) {
byte[] subBuffer = new byte[3];
subBuffer[0] = buffer[i++];
subBuffer[1] = buffer[i++];
if (IS_24_BITS) {
subBuffer[2] = buffer[i++];
}
int currentValue = 0;
if (AUDIO_FORMAT.isBigEndian()) {
if (IS_24_BITS) {
currentValue += subBuffer[0] << 16;
currentValue += subBuffer[1] << 8;
currentValue += subBuffer[2];
} else {
currentValue += subBuffer[0] << 8;
currentValue += subBuffer[1];
}
} else {
if (IS_24_BITS) {
currentValue += subBuffer[2] << 16;
}
currentValue += subBuffer[0];
currentValue += subBuffer[1] << 8;
}
if (currentValue > max) {
max = currentValue;
}
}
SOUNDMETER.setVolume(max);
}
}
TARGET_DATA_LINE.close();
System.out.println("Thread " + getName() + " stopped!");
}
}
Он запускает интерфейс ввода при инициализации, а затем сразу запускается сам.
Класс SoundMeter - это внешний класс, который управляет микшером для использования и инициализирует поток, а также вызывает прерывание для объекта потока (Редактировать: этот класс также получает вычисленные значения объема).
Я думаю, что проблема в методе блокировки TargetDataLine.read(), но я не знаю, как это решить.




Thread.interrupt () обычным способом просто выставляем флаг и все
If none of the previous conditions hold then this thread's interrupt status will be set.
Таким образом, ваш поток будет завершен во время первой проверки, а (! IsInterrupted ()) после вашего вызова Thread.interrupt ()
Вместо использования API прерывания я бы просто использовал в этом случае флаг booleanisRunning и установил его на false, когда поток должен остановиться.
Флаг, установленный interrupt(), может быть нам незаметен, особенно когда поток блокируется:
If this thread is blocked in an invocation of the
wait(),wait(long), orwait(long, int)methods of theObjectclass, or of thejoin(),join(long),join(long, int),sleep(long), orsleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive anInterruptedException.
Поскольку TargetDataLine.read сообщает, что он может блокироваться, нет никаких оснований ожидать, что isInterrupted() будет работать здесь.
Было бы хорошо, если бы TargetDataLine снова прервал себя, чтобы установить для нас флаг, когда он получит InterruptedException, но ничто не говорит о том, что это необходимо.
Хотя я знал эту часть документации, я подумал, что она не подойдет для моего случая, поскольку я не использую ни один из упомянутых методов. Однако настраиваемый логический флаг работал (как я и ожидал), но я думал, что это то, для чего существует API прерывания, поэтому я сначала попробовал этот подход. Похоже, что это не так (или не в каждом случае использования в этом отношении), к сожалению.
Когда документ TargetDataLine.read говорит, что он блокируется, это означает, что он вызывает один из этих методов, что означает, что он иногда ловит InterruptedException. Например, в Mac OS реализация TargetDataLine - это com.sun.media.sound.DirectAudioDevice$DirectTDL, ну и Вы можете видеть, что звоните, подождите прямо здесь, и она просто поглощает исключение.
Так что он делает то, что нам всем велели не делать :(
Скорее всего, это не связано, но стоит отметить, что многие необычные проблемы возникают из-за расширения класса Thread. Мы должны избегать этого. Вместо этого всегда предпочитайте реализовывать класс, представляющий задачу, которая в Java является Runnable, и передавать эту задачу экземпляру Thread (который представляет собой работника, обрабатывающего эту задачу).