I coded up two more examples to test the yield strategy. The “split synchronized” example doesn’t work that well (yet), because it fails even without the added yields. I’m looking for common bugs that aren’t obvious.
Thread remover = new Thread() {
public void run() {
while(l.size()>0) {
l.remove(0);
}
}
};
Thread reader = new Thread() {
public void run() {
for(int i=0; i<10; ++i) {
if (l.size()==0) { break; }
int e = l.get(l.size()-1); // l.size and l.get not atomic
}
}
};
The important thing here is that the calls to l.size()
and l.get
may get interrupted by the other thread performing an l.remove(0)
. I know that this happens quite frequently and is not always as obvious as here (in DrJava, this happened when we tried to append text to a document; append was implemented "insert text at index", and the length was used as index), but right now I'm unable to reproduce that in a short example.
However, I have another example from my COMP 402 course last semester that benefits from the inserted yields:
class WaitRunnable implements Runnable {
public void run() {
sleep(1000);
while(true) {
synchronized(_lock) {
if (_flag) break;
}
// lock dropped here!
synchronized(_lock) {
try { _lock.wait(); }
catch(InterruptedException ie) { /* ignore */ }
}
}
}
}
The problem here is that the condition _flag
is checked, but before wait()
is called, the lock is dropped. Therefore, another thread may call notify()
and change _flag
and allow the first thread to enter a wait()
from which it will not be woken up.