在项目多线程编程中用了ReentrantLock配合Condition来控制线程的加锁和解锁:
private void signalAllConnect() {
final ReentrantLock lock = this.connectLock;
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
SyncLogUtil.e(e);
} finally {
connectCondition.signalAll();
SyncLogUtil.d("notify the connect task...");
lock.unlock();
}
}
之前的代码是这么写的,采用lockInterruptibly()来上锁,结果随机性出现以下异常:
java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:123)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1235)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:429)
at com.xtc.sync.connection.TCPConnection.putConnectTaskToQueue(TCPConnection.java:435)
at com.xtc.sync.connection.TCPConnection.connect(TCPConnection.java:410)
at com.xtc.sync.connection.TCPConnection.connect(TCPConnection.java:339)
at com.xtc.sync.connection.ConnectionService.connect(ConnectionService.java:288)
at com.xtc.sync.connection.ConnectionService.decodeData(ConnectionService.java:414)
at com.xtc.sync.connection.ConnectionService.access$1600(ConnectionService.java:52)
at com.xtc.sync.connection.ConnectionService$5.onRead(ConnectionService.java:381)
at com.xtc.sync.connection.ReadAndWriteDataThread.onRead(ReadAndWriteDataThread.java:156)
at com.xtc.sync.connection.ReadAndWriteDataThread.run(ReadAndWriteDataThread.java:109)
意思是该线程还未被lock,然后就调用了unLock造成的异常,跟进源码里面发现异常是这边报出来的:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
而getExclusiveOwnerThread()会在线程被lock的时候调用的,也就是说线程确实还没被上锁就被unLock了,因为我用的是lockInterruptibly(),所以在线程调用interrupt()的时候这里会抛出InterruptedException异常,然后线程lock就失败,但是后面的finally里面却有对线程执行了unLock,所以就报错了,解决方法如下:
private void signalAllConnect() {
final ReentrantLock lock = this.connectLock;
try {
lock.lockInterruptibly();
try {
connectCondition.signalAll();
} finally {
SyncLogUtil.d("notify the connect task...");
lock.unlock();
}
} catch (InterruptedException e) {
SyncLogUtil.e(e);
}
}
改成如上写法,当lockInterruptibly()抛出异常的时候就不会执行unlock()方法了,而且我看了BlockQueue的一些阻塞实现也是类似如上写法,它从来不会把unLock操作和lockInterruptibly操作放在同一级,而是把unlock操作放在lockInterruptibly操作的下一步,保证lockInterruptibly()抛出异常后,不执行unlock