在項目多線程編程中用了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