Java:SourceDataLine.write(…)掩码中断吗?

Java:SourceDataLine.write(…)掩码中断吗?,java,audio,interrupt,Java,Audio,Interrupt,我有一个线程循环调用SourceDataLine.write(…),直到所有音频数据都被写入(播放)。我希望能够提前停止播放(在循环通常会被EOF类型的条件终止之前),并使用中断进行am(尝试)。当我调用playbackThread.interrupt()时,它只会偶尔中断和终止线程。代码中的微小更改或连续调用会产生不同的(看似随机的)结果。我已经调试并尝试使用Thread.currentThread.isInterrupted()而不是Thread.interrupted()。我已经注意到,如

我有一个线程循环调用SourceDataLine.write(…),直到所有音频数据都被写入(播放)。我希望能够提前停止播放(在循环通常会被EOF类型的条件终止之前),并使用中断进行am(尝试)。当我调用playbackThread.interrupt()时,它只会偶尔中断和终止线程。代码中的微小更改或连续调用会产生不同的(看似随机的)结果。我已经调试并尝试使用Thread.currentThread.isInterrupted()而不是Thread.interrupted()。我已经注意到,如果我用其他东西(例如,一个长的计数循环)替换SourceDataLine.write(…)调用,那么线程确实会以可预测且一致的方式终止

SourceDataLine.write(…)“使用”中断但不抛出中断异常吗?也就是说,它是否清除了中断标志(可能是使用Thread.interrupted()),忽略它,然后继续?这可以解释为什么中断是间歇性工作的:如果在SDL.write(…)方法中发生中断,它将被丢弃;否则,它将保持“完整”,直到我使用Thread.interrupted()调用进行测试。这将是可怕的,但这是我花了一整天的时间才想到的唯一解释。我找不到SourceDataLine.write(…)的任何源,因为它是一个接口,而且(我想)实现依赖于机器

由于我没有使用try/catch并希望在循环中捕获InterruptedExection,而是在每次循环的底部调用Thread.interrupted()(或Thread.currentThread.isInterrupted()),因此我可以轻松创建自己的interruptFlag并测试它。这正是我几个月前在其他地方中断TargetDataLine.read(…)方法时所做的事情,当时我正急于满足一个计划。我周末的大部分时间都在一劳永逸地调查这个长期悬而未决的谜团的根源,但没有成功

使用实际的Java中断机制似乎更为一致/优雅,但如果它不起作用就不行了。这个解释可信吗

playbackThread的代码:

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();
}
更新:

仍在调查,以更全面地了解这里发生了什么。在write()调用之前和之后,我通过从写循环本身调用Thread.interrupt()来强制中断playbackThread,并插入对Thread.currentThread().isInterrupted()的调用,从而不清除测试本身的中断标志。我发现,对于前几个循环,从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();
}

问题是大部分时间都花在
cOutputLine.write
上,它不会因为中断而停止。这意味着您需要在循环检查中断之前写入一些数据。如果这阻塞(由于缓冲区已满),它将永远不会检查中断


解决这个问题的一种方法是关闭流,这将在写操作中触发IOException。或者您可以使用阻塞NIO(它不必是非阻塞的),当线程被中断时,它会停止。

您遇到的问题是,大部分时间都花在
cOutputLine上。写入
,它不会因为中断而停止。这意味着您需要在循环检查中断之前写入一些数据。如果这阻塞(由于缓冲区已满),它将永远不会检查中断


解决这个问题的一种方法是关闭流,这将在写操作中触发IOException。或者您可以使用阻塞NIO(它不必是非阻塞的),当线程被中断时,阻塞NIO会停止。

事实证明,SourceDataLine.write()会在写入期间阻塞,如果在写入期间发生iterrupt,则会忽略它并重置中断标志。无法直接检测中断。我想这是使用Java音频的人们的常识,如果不理解这一点,就无法创建可靠的可中断音频播放。我将(遗憾地)不得不求助于在每次写入()之后读取的syncrhonized标志来检测中断。

事实证明,SourceDataLine.write()在写入过程中会阻塞,如果在写入过程中发生iterrupt,则会忽略它,并重置中断标志。无法直接检测中断。我想这是使用Java音频的人们的常识,如果不理解这一点,就无法创建可靠的可中断音频播放。我将(遗憾地)不得不求助于在每次写入()后读取的syncrhonized标志来检测中断。

我使用的解决方案是在
SourceDataLine
对象上查询
line.available()
,以获得可以无阻塞写入的字节数。我观察到,如果在
SourceDataLine
上执行
write
操作的线程在阻塞写入期间被中断(即写入的字节数超过
line.available()
调用返回的值),那么中断标志似乎没有设置,对
thread.interrupted()的调用也没有设置
返回
false

我使用的解决方案是在
SourceDataLine
对象上查询
line.available()
,以获得无阻塞情况下可以写入的字节数。我观察到,如果在
SourceDataLine
上执行
write
操作的线程在一个块期间被中断
public void run() {
    int offset = 0;
    int remaining = cRawAudioBuffer.length;
    int length = WRITE_LENGTH;
    int bytesWritten = 0;
    cOutputLine.start();

    ExecutorService executor = Executors.newSingleThreadExecutor();

    while (remaining>0) {
        try {
            executor.submit(() -> {
                length = Math.min(length,remaining);
                bytesWritten = cOutputLine.write(cRawAudioBuffer,offset,length);
                offset += bytesWritten;
                remaining -= bytesWritten;
            }).get();
        } catch (InterruptedException interruptedException) {
            Thread.currentThread().interrupt();
        }
    }
    cOutputLine.close();
}