I recently discovered that using synchronized won't prevent any dead locks.
E.g. within this code:
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
Now, what is the problem? Well, if the running thread isn't waiting, he won't see any notifications (i.e. notify() calls), therefore he may run into a dead lock and not handle the tasks he received! (Or he may handle them too late...)
Therefore I implemented this code:
private volatile boolean notifier = false;
ArrayList <Job> task;
...
public void do(Job job){
synchronized(tasks){
tasks.add(job);
}
synchronized(this){
notifier = true;
notify();
}
}
public void run(){
while(true){
for (int = 0;i<tasks.size();i++){
synchronized(tasks){
Job job = tasks.get(i);
}
//do some job here...
}
synchronized(this){
if(!notifier){
wait(); //lock will be lost...
notifier = false; //lock will be acquired again after notify()
}
}
}
}
Is this correct or am I missing something? And can it be done easier?
The result actually isn't a dead lock, but rather a starvation of the task/job itself. Because no threads are "locked", the task just won't be done until another thread calls
do(Job job)
.Your code is almost correct - beside the missing exception handling when calling
wait()
andnotify()
. But you may put thetask.size()
within a synchronisation block, and you may block the tasks during the hole process because a deletion of a job within tasks by another thread would let the loop to fail:Just note that your code is blocking. Non-blocking might be faster and look like this:
What can we learn? Well, within non-blocking threads no
notify()
,wait()
andsynchronized
are needed. And setting wait(1) doesn't even use more CPU when idle (don't set wait(0) because this would be treated as wait().However, be careful because using
wait(1)
may be slower than usingwait()
andnotify()
: Is wait(1) in a non-blocking while(true)-loop more efficient than using wait() and notify()? (In other words: Non-blocking might be slower than blocking!)Right. This is not a case of being "unreliable" but rather a case of language definition. The
notify()
call does not queue up notifications. If no threads are waiting then thenotify()
will effectively do nothing.Yes. I'd look into using
BlockingQueue
-- aLinkedBlockingQueue
should work well for you. One thread call pull from the queue and the other can add to it. It will take care of the locking and signaling for you. You should be be able to remove a large portion of your hand written code once you start using it.I was tricked by your question at first. Your synchronize(this) on thread object don't make sense. I in the past also do this stuff to make wait() not throwing compilation error.
Only synchronize(tasks) make sense as you are waiting and want to acquire this resources.
Having a for loop, it is bad design. In the consumer-producer problem. get a job at the same time remove a job. better fetch a job once at a time.