我有一些我认为非常简单的代码:
public int internalWrite(byte[] data, int offset, int size) throws InterruptedException {
lock.lockInterruptibly();
try {
if (state == State.RELEASED) throw new TrackReleasedException();
return track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
} finally {
if (!lock.isHeldByCurrentThread()) {
Log.e("phonographnative", "internalWrite() lock is not held by current thread! " + Thread.currentThread());
} else lock.unlock();
}
}
lock
是一个fair
ReentrantLock,但非fair
的问题也会出现。 track
是Android AudioTrack;它的write
方法主要是本机代码(但与线程无关)。无论如何,它无权访问lock
。实际上,实践中永远不会抛出该异常(并且永远不会在研究此行为时抛出该异常)。发生的事情是,该锁将非常可重复地生成(稍后将对此进行详细介绍),该锁将在同一线程内神秘地被解锁,从而导致出现日志消息。以前,当我没有进行此检查时,可能会引发IllegalMonitorStateException。发生几次之后,锁的代码本身中将出现一个java.lang.AssertionError: Attempt to repark
。一些示例性的日志输出:
2019-03-20 12:20:37.428 8097-8181/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
2019-03-20 12:20:37.428 8097-8184/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
2019-03-20 12:20:37.428 8097-8181/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
2019-03-20 12:20:37.428 8097-8184/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
2019-03-20 12:20:37.430 8097-8184/com.kabouzeid.gramophone.debug E/AndroidRuntime: FATAL EXCEPTION: phonographnative-decoding-65308.0
Process: com.kabouzeid.gramophone.debug, PID: 8097
java.lang.AssertionError: Attempt to repark
at java.lang.Thread.parkFor$(Thread.java:2143)
at sun.misc.Unsafe.park(Unsafe.java:325)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312)
at com.kabouzeid.gramophone.service.ffmpeg.AudioContext.internalWrite(AudioContext.java:197)
at com.kabouzeid.gramophone.service.ffmpeg.AudioContext.write(AudioContext.java:177)
at com.kabouzeid.gramophone.service.ffmpeg.FFmpegPlayer.decodeAndPlayAudio(Native Method)
at com.kabouzeid.gramophone.service.ffmpeg.FFmpegPlayer.lambda$new$0(FFmpegPlayer.java:72)
at com.kabouzeid.gramophone.service.ffmpeg.-$$Lambda$FFmpegPlayer$MKAlsDZBJzprKYoChfgA-0JlIi8.run(lambda)
at java.lang.Thread.run(Thread.java:761)
即使没有其他线程尝试运行此代码,并且即使我注释掉了所有其他尝试锁定/解锁此锁定的尝试(由其他线程在不同的代码节中),也会发生这种情况。此问题很少间歇性发生,但是当我尝试取消暂停正在写入的AudioTrack时,此问题确实可以发生。最初写入它或执行其他任何操作(例如从头开始播放)时,它不会发生。这种暂停是在完全不同的线程上发生的,并且我无法确定两件事之间的因果关系。可能只是一些随机的调度程序疯狂。
internalWrite
方法被非常频繁地调用,每秒大约数千次。我觉得这只是Android ReentrantLock
实现中的一个错误,因为命中的断言完全在JVM代码中。 (“试图停车”甚至是什么意思?)但是我不能排除我错过了自己代码中的其他细节,并且对此表示感谢!
完整代码可在here中找到。我还跟踪了Android代码中的相关部分:AudioTrack#write,native_write_byte(方法的名称有所不同),writeToTrack,AudioTrack->write。但是,他们根本没有帮助我阐明此错误。
答案 0 :(得分:0)
实际上,您的代码可能会在内部调用时释放Lock / lock.unlock()
track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
当您尝试在finally块中第二次释放锁/ lock.unlock()
时,它对当前线程没有任何锁,因此根据您的代码逻辑它抛出错误。
如果要释放lock.unlock()
中的Lock / track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
如果您无法删除lock.unlock()
内的track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
然后您执行“双重锁定” /两次获取锁定,然后释放或解锁两次,您的问题将得到解决...
更好地理解示例代码:
import java.util.concurrent.locks.ReentrantLock;
public class Main
{
private final static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
try {
System.out.println("Hello World:::::"+internalWrite());
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
public static int internalWrite() throws InterruptedException{
lock.lockInterruptibly();
lock.lockInterruptibly();// Comment this line to reproduce your issue
try {
if(lock.isHeldByCurrentThread())
System.out.println("lock::::"+lock.isHeldByCurrentThread());
lock.unlock();
System.out.println("lock::::"+lock.isHeldByCurrentThread());
return 1;
} finally {
if (!lock.isHeldByCurrentThread()) {
System.out.println("phonographnative internalWrite() lock is not held by current thread! " + Thread.currentThread());
} else lock.unlock();
}
}
}
答案 1 :(得分:0)
如果您能够删除-
内的lock.unlock()
然后您的当前代码可以正常运行,而无需进行任何修改。
(OR)
如果您无法删除track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
内的lock.unlock()
当前代码需要修改为两次获取锁:
track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
lock.lockInterruptibly();
lock.lockInterruptibly();
答案 2 :(得分:0)
这是怎么回事:
lock
并开始工作。lock
并被阻塞,因为A尚未在unlock()
上调用lock
。lock
。finally
块由B执行。B没有获取lock
,因为当中断发生时A没有释放它。因此,lock.isHeldByCurrentThread()
对B是false
。不是假设if (!lock.isHeldByCurrentThread())
块中的finally
是错误的,也不是B的有害状态,您可以继续操作而不记录错误/警告,并且也可以不要unlock()
lock
。
public int internalWrite(byte[] data, int offset, int size) throws InterruptedException {
try {
lock.lockInterruptibly(); // if outside the try block the next lines would be executed in the described situation without having acquired the lock
if (state == State.RELEASED) throw new TrackReleasedException();
return track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}