Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a blocking queue in JavaME: how to optimize it?

I'm trying to implement a simple blocking queue in Java ME. In JavaME API, the concurrency utilities of Java SE are not available, so I have to use wait-notify like in the old times.

This is my provisional implementation. I'm using notify instead of notifyAll because in my project there are multiple producers but only a single consumer. I used an object for wait-notify on purpose to improve readability, despite it wastes a reference:

    import java.util.Vector;

    public class BlockingQueue {    
        private Vector queue = new Vector();
        private Object queueLock = new Object();    

        public void put(Object o){
            synchronized(queueLock){
                queue.addElement(o);
                queueLock.notify();
            }       
        }

        public Object take(){
            Object ret = null;
            synchronized (queueLock) {
                while (queue.isEmpty()){
                    try {
                        queueLock.wait();
                    } catch (InterruptedException e) {}
                }

                ret = queue.elementAt(0);
                queue.removeElementAt(0);
            }
            return ret;
        }
    }

My main question is about the put method. Could I put the queue.addElement line out of the synchronized block? Will performance improve if so?

Also, the same applies to take: could I take the two operations on queue out of the synchronized block?

Any other possible optimization?

EDIT:
As @Raam correctly pointed out, the consumer thread can starve when being awakened in wait. So what are the alternatives to prevent this? (Note: In JavaME I don't have all these nice classes from Java SE. Think of it as the old Java v1.2)

like image 449
Mister Smith Avatar asked Nov 26 '22 00:11

Mister Smith


2 Answers

The Vector class makes no guarantees to be thread safe, and you should synchronize access to it, like you have done. Unless you have evidence that your current solution has performance problems, I wouldn't worry about it.

On a side note, I see no harm in using notifyAll rather than notify to support multiple consumers.

like image 185
claesv Avatar answered Dec 05 '22 18:12

claesv


synchronized is used to protect access to shared state and ensure atomicity.

Note that methods of Vector are already synchronized, therefore Vector protects it own shared state itself. So, your synchronization blocks are only needed to ensure atomicity of your operations.

You certainly cannot move operations on queue from the synchronized block in your take() method, because atomicity is crucial for correctness of that method. But, as far as I understand, you can move queue operation from the synchronized block in the put() method (I cannot imagine a situation when it can go wrong).

However, the reasoning above is purely theoretical, because in all cases you have double synchronization: your synchronize on queueLock and methods of Vector implicitly synchronize on queue. Therefore proposed optimization doesn't make sense, its correctness depends on presence of that double synchronization.

To avoid double synchronization you need to synchronize on queue as well:

synchronized (queue) { ... }

Another option would be to use non-synchronized collection (such as ArrayList) instead of Vector, but JavaME doesn't support it. In this case you won't be able to use proposed optimization as well because synchronized blocks also protect shared state of the non-synchronized collection.

like image 20
axtavt Avatar answered Dec 05 '22 18:12

axtavt