我有一个循环调用 SourceDataLine.write(...) 的线程,直到所有音频数据都被写入(播放)。我希望能够过早地停止播放(在循环通常会被 EOF 类型的条件终止之前)并且我(尝试)使用中断。当我调用playbackThread.interrupt() 时,它只会偶尔导致线程中断和终止。代码中的微小更改或连续调用会产生不同的(看似随机的)结果。我已经调试并尝试使用 Thread.currentThread.isInterrupted() 而不是 Thread.interrupted()。我已经注意到,如果我用其他东西(例如长计数循环)替换 SourceDataLine.write(...) 调用,线程确实会以可预测且一致的方式终止。
SourceDataLine.write(...) 是否“消耗”中断但不抛出 InterruptedException?意思是,它是否清除中断标志(可能使用 Thread.interrupted()),忽略它,然后继续?这可以解释为什么中断会间歇性地工作:如果它发生在 SDL.write(...) 方法中,它会被丢弃;否则,在我使用 Thread.interrupted() 调用进行测试之前,它会保持“完整”。这将是可怕的,但这是我花了一整天时间后能想出的唯一解释。我找不到 SourceDataLine.write(...) 的任何来源,因为它是一个接口,并且(我想)实现是依赖于机器的。
由于我没有使用 try/catch 并期望在循环中捕获 InterruptedExeption,而是在每次循环的底部调用 Thread.interrupted() (或 Thread.currentThread.isInterrupted()),我可以轻松创建我自己的interruptFlag 并对其进行测试。这正是我几个月前在急于赶上日程时中断 TargetDataLine.read(...) 方法时在其他地方所做的。我周末的大部分时间都在彻底调查这个长期谜团的根源,但没有成功。
使用实际的 Java 中断机制似乎更加一致/优雅——但如果它不起作用,则不然。这个解释靠谱吗?
回放线程代码:
public void run() {
int offset = 0;
int remaining = cRawAudioBuffer.length;
int length = WRITE_LENGTH;
int bytesWritten = 0;
cOutputLine.start();
while (remaining>0) {
length = Math.min(length,remaining);
bytesWritten = cOutputLine.write(cRawAudioBuffer,offset,length);
offset += bytesWritten;
remaining -= bytesWritten;
if (Thread.currentThread().isInterrupted()) {
System.out.println("I Was interrupted");
break;
}
}
cOutputLine.close();
}
更新:
仍在调查以更全面地了解这里发生的事情。我通过从写循环本身内部调用 Thread.interrupt() 来强制中断播放线程,并插入对 Thread.currentThread().isInterrupted() 的调用 - 因此没有用测试本身清除中断标志 - 之前和在 write() 调用之后。我发现对于前几个循环,从 write() 方法返回后保留了中断。前 3 次循环后,从 write() 返回后清除中断。此后,它几乎总是,但不是总是,从 write() 返回后清除。我的猜测是,如果 write() 被阻塞以等待其缓冲区清除,它将检测、忽略并清除中断标志。仍然只是一个猜测,我更愿意确定(以避免传播我做错的事情并让它以后咬我)。关于如何在不访问来源的情况下验证这个假设的任何想法?
public void run() {
....
while (remaining>0) {
length = Math.min(length,remaining);
Thread.currentThread().interrupt();
System.out.println("Player interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set before write()");
bytesWritten = cOutputLine.write(cRawAudioBuffer,offset,length);
System.out.println("Player interrupt flag is " + (Thread.currentThread().isInterrupted()?"":"not ") + "set after write()");
offset += bytesWritten;
remaining -= bytesWritten;
if (Thread.interrupted()) {
System.out.println("I Was interrupted");
//break;
}
}
cOutputLine.close();
}