Lock and semaphore, what's better method for my case
Hi all,
I'm implementing a classic case: a consumption queue with infinite capacity. That's say I have a queue of infinity capacity, a thread to put objects into the queue, another thread take it out. Pseudo code is smth like below:
void put(Object o) {
put message into the queue
if (consume thread is waiting) {
lock();
signal();
unlock();
}
}
void eat() {
if (queue not empty)
take object out;
else
lock;
wait for signal from producer;
wake up and take object out;
unlock;
}
I don't know if I should use semaphore or Lock interface to get the job done. Do you know which one is better in my case? I'm writing an apps which is very sensitive in latency so basically I want the eater to get the object as soon as possible. Any advices?
Message was edited by:
principles
# 1
First, what is your lock() for? If it's to protect the data structure while you're updating it, you're calling it in the wrong place. If it's there to protect the signal/wait for signal mechanism it is also being called in the wrong place because it will produce a deadlock whenever the consumer is waiting and the producer has produced. You need to go over this again.
Having said that, I don't really see why you can't just use classical synchronization in this situation. I don't see any particular benefit from using a Lock, as e.g. there is no shared-read access in this situation, and no multiple locks that might have to be released in a different order from the acquisition order. I don't see a benefit from using a Semaphore either, as there are only two threads concerned. But you could use any of these three.
ejpa at 2007-7-11 15:24:09 >

# 2
I agree. It could be a deadlock if the consumer wait but some messages are already there. However it's not my concern, I could use another thread to periodically check the deadlock and release it.
My main concern is: if properly implemented, what are the performance of these 3? I'm looking for a algorithms that minimize the time from producer put the object into the queue until the consumer takes the object out. It's really important to me.
The number of objects producer could produce is quite random. It could be up to 1 object every 5us in a short period of time (a few ms).
# 3
But I still don't understand what the lock is actually for ...
ejpa at 2007-7-11 15:24:09 >

# 4
If I don't lock, how I could make the signaling?You could argue of using wait/notify. But then, the JVM uses implicit lock in that case.
# 5
I don' t understand why you need a lock for the signalling and I also don't understand why you don't need a lock for the data structure.I would indeed use wait() and notify() in this case, or more probably just a BlockingQueue that would handle the whole megillah for me.
ejpa at 2007-7-11 15:24:09 >

# 6
If you know a mechanism in Java who permit signaling without locking, let me know and I'll be more than glad to try it. Wait/notify uses synchronization, which is kind of implicit locking.
Blocking queue doesn't work for me as it is too slow. I don't need to lock the data because one thread adds too the tail, one thread consumes the head, so why bother?
# 7
OK, you need a Lock if you're using Condition.await()/signal() and you need a synchronized() if you use Objecty.wait()/notify(). I still can't see a reason to go beyond wait/notify as you don't need any of the extra facilities of Lock.
ejpa at 2007-7-11 15:24:09 >

# 8
My main question is which one is faster? Wait/Notify is safe as the JVM takes care of everything for you. However rumors has it that Lock has better performance than wait/notify mechanism.
# 9
You'll have to benchmark it and see for yourself on the platforms in question. Most probably it won't make much difference, but how much is too much only you can answer.
ejpa at 2007-7-11 15:24:09 >

# 10
> Blocking queue doesn't work for me as it is too slow.
> I don't need to lock the data because one thread
> adds too the tail, one thread consumes the head, so
> why bother?
LinkedBlockingQueue allows concurrent access to the head and tail ie it uses two different locks.
A Lock is a mechanism for mutual exclusion. It generally has a notion of ownership and so the thread that locks must be the thread that unlocks.
A Semaphore is a resource counter. There are no constraints on which thread signals() after await(). It is only a protocol that you establish in your code that allows it to be used for exclusion.
A bounded-buffer generally needs two synchronization aids:
a) an exclusion control to ensure the data structure is not corrupted by concurrent access
b) a coordination control to allow consumers to block when the buffer is empty, or producers to block when the buffer is full.
These two can be combined by using "synchronized" methods and wait/notify. Or by using Lock and associated Condition objects. Or you can use "synchronized" blocks or Lock for exclusion, and handle the coordination using a seperate semaphore (which must use some form of internal synchronization too - but perhaps more efficient.)
If you have a lock-free data structure, such as ConcurrentLinkedQueue then you don't need anything for (a) and so you only need coordination. So try using a Semaphore: put() increments it and take() decrements it.
But the Semaphore still becomes a serialization point in your code.
