Using spin lock in your code
Today, my friend ask me to review some code of him, his code will encounter some unexpected error. He's very confused and worried. After digging into the code, I found the problem.Here's the code slice :
private boolean done;
private Message[] messages;
... ...
synchronized(this) {
if(!done || messages ==null) // 1
try {
wait(); // 2
} catch (InterruptedException e) {
e.printStackTrace();
}
...
dispatchMessage(messages); // 3
...
}
dispatchMessage(Message[] messages) {
int length = messages.length; // 4
... ...
messages = null;
}
When the program run, it will throw NullPointerException sometimes, though very infrequent. And just because of its infrequence, it's hard to debug.private Message[] messages;
... ...
synchronized(this) {
if(!done || messages ==null) // 1
try {
wait(); // 2
} catch (InterruptedException e) {
e.printStackTrace();
}
...
dispatchMessage(messages); // 3
...
}
dispatchMessage(Message[] messages) {
int length = messages.length; // 4
... ...
messages = null;
}
But when go through this code slice, I think Java Expert can learn where's the problem.
Try to imagine such scenario:
1) messages is null
2) Thread 1 check the condition in position 1and waiting in position 2, and it will release the lock, So other thread can obtain it;
3) Thread 2 check the condition in position 1and waiting in position 2 too;
4) When we get the message and notify all threads using notifyAll(), both thread 1 and thread 2 will be awaked but only one thread will obtain the lock.
5) Assume thread 1 will get the lock, and it will dispatch the message using dispatchMessage. after dispatching the message, it unreference the messages.
6) When thread 1 finished dispatching the message and release the lock. Thread 2 will obtain the lock, however, NOW, messages is null, so it will throw NPE.
Then, how to solve the problem? Yeah, it should use spin lock here. So, it will re-check the condition after being awaked and will avoid such problem.
That is, it should be
while(!done || message ==null)
... ...
And this trick is called spin lock(reference1) , you can see detail discussion in this book (practice 54).
BTW: You also can find this trick in "Effective Java", in item 50 - "Never use wait out of loop". And, this two book is really good and worth reading. :-)
Reference:
1) "Pratical Java" - Peter Haggar
2) "Effective Java" - Joshua Bloch
( 2004年12月14日, 09:29:59 下午 GMT+08:00 ) Permalink 评论 [6]
发表一条评论:
该日志评论功能被禁用了。


发表于 Grahamm 在 2004年12月15日, 01:41 上午 GMT+08:00 #
Yes, his solution is not so good, it's better to design an more fair way "that only wake up threads that need to be woken up when work is available" :P. BTW: notifyAll will awake all the waiting thread in an unexpected order. So, if we wanna the thread to be awaked in an expected order, we must give own solution. Such as "Specific Notification Pattern".
发表于 Elan Meng 在 2004年12月15日, 11:31 上午 GMT+08:00 #
发表于 Winters.Mi 在 2004年12月15日, 02:54 下午 GMT+08:00 #
发表于 Elan Meng 在 2004年12月15日, 03:12 下午 GMT+08:00 #
发表于 Elan Meng 在 2004年12月15日, 03:12 下午 GMT+08:00 #
The author of this article is Peter Hagger - the author of "Pratical Java".
发表于 Elan Meng 在 2004年12月15日, 03:14 下午 GMT+08:00 #