Reputation: 2040
I have a queue with some blocking mechanism in "Add" and "Get" methods, where first thread adds data and second thread gets data.
public synchronized MyObj getData() {
synchronized (myLock) {
synchronized (this) {
if (isEmpty()) {
wait(0);
}
}
return getData();
}
}
public synchronized void addData(MyObj data) {
if (!isFull()) {
putData(data);
synchronized (this) {
notify();
}
}
}
In the code above, if first thread tries to get data and queue is empty i put in wait via wait(0) until other thread add data to queue an release from waiting via notify().
Now I want to add another "lock" when queue is full and some one tries to add more data to it:
public synchronized MyObj getData() {
synchronized (myLock) {
synchronized (this) {
if (isEmpty()) {
wait(0);
}
}
synchronized (this) {
notify();
}
return getData();
}
}
public synchronized void addData(MyObj data) {
synchronized (myLock) {
synchronized (this) {
if (isFull()) {
wait(0);
}
}
}
synchronized (this) {
notify();
}
PutData(data);
}
The result is not what I expect , I guess that i get a dead lock cause process is stuck.
UPDATE
This is how I get data:
queueSize--;
startPointer = (startPointer + 1) % mqueueSize;
data = (String) queue[startPointer];
this is how i add data
queueSize++;
endPointer = (endPointer + 1) % mqueueSize;
queue[endPointer] = data;
public synchronized boolean isEmpty() {
return queueSize== 0;
}
public synchronized boolean isFull() {
return queueSize== mqueueSize;
}
Upvotes: 1
Views: 391
Reputation: 13789
As already mentioned, your code has too many synchronized
annotations. Also, the condition to wait
on is checked in an if
conditional, but it should ideally be checked in a while
loop to avoid spurious wakeups. Here is the outline of the code that fixes these.
// _isEmpty and _getData are private unsynchronized methods
public MyData get() throws InterruptedException {
// wait and notify should be called from a block
// synchronized on the same lock object (here myLock)
synchronized (myLock) {
// the condition should be tested in a while loop
// to avoid issues with spurious wakeups
while (_isEmpty()) {
// releases the lock and wait for a notify to be called
myLock.wait();
}
// when control reaches here, we know for sure that
// the queue is not empty
MyData data = _getData();
// try to wake up all waiting threads - maybe some thread
// is waiting for the queue not to be full
myLock.notifyAll();
}
}
// _isFull and _putData are private unsynchronized methods
public void put(MyData obj) throws InterruptedException {
synchronized (myLock) {
while (_isFull()) {
myLock.wait();
}
_putData(obj);
myLock.notifyAll();
}
}
Upvotes: 0
Reputation: 62459
Why do you have three synchronized
statements? The wait(0)
only releases the lock on this
, so just keep that one and dump the synchronized
from the method and the synchronized(myLock)
.
Whenever you call wait on some object (in this case you are calling on this
), the lock on that object is automatically released to allow the other thread to proceed. But you are never calling wait on myLock
(and nor should you, because you are calling on this
already). That part is redundant and causes the deadlock.
Consider this scenario: the thread that is supposed to add takes the lock on myLock
but finds the queue full, so it waits. This wait does not release the lock on myLock
. The other thread wants to take data but cannot enter the synchronized
block because the first thread did not release the lock on myLock
.
Conclusion: remove the synchronized(myLock)
blocks.
Upvotes: 3
Reputation: 38235
Remove the synchronized
keyword from your method signatures, as that implies you hold the this
monitor for the whole method call -- the synchronized(this)
blocks are simply redundant.
EDIT:
...Then call wait and notify on myLock
rather than this
. And forget completely about synchronizing on this
. This is because while waiting (on this
in your current code), you're not releasing the myLock
lock, so the other thread is not able to get to notify()
.
Upvotes: 1
Reputation: 18330
Replace if
with while
. It won't hurt to double check, if the collection really become not empty/not full.
You don't really need two locks. Single lock will work almost as well and should be much simpler.
public synchronized T get()
{
while(isEmpty())
wait(0);
notifyAll();
return super.get();
}
public synchronized put(T t)
{
while(isFull())
wait(0);
super.put(t);
notifyAll();
}
All threads will wake up when something changes. But if they can't do their work, they will wait
for next notify
.
Upvotes: 0
Reputation: 1530
Why you don't take a look in java.util.BlockingQueue. Probably it will be useful in your situation.
Particularly take a look at java.util.LinkedBlockingQueue, where if you specify the queue's capacity in the constructor, then the queue will block.
Upvotes: 1