3

Say we create a thread which runs a synchronized method. This method tries to take() from an empty blocking queue. Now let a separate thread then try to put() and element onto the blocking queue while synchronized on the same object.

This causes a deadlock:

  • The first thread will not release the lock until an element is added to the queue.
  • The second thread cannot add an element until the lock is free for it to acquire.

If the two actions need to be atomic and run on separate threads, how can this be achieved without causing a deadlock?

I understand that take() and put() are thread-safe. My question is for when they are used as part of larger actions that must be atomic.

Example:

import java.util.concurrent.*; public class DeadlockTest { String input = "Nothing added yet!"; LinkedBlockingQueue<String> buffer = new LinkedBlockingQueue<>(); public synchronized String getFromBuffer() { System.out.println("Trying to get input from buffer."); try { input = buffer.take(); } catch (InterruptedException ex) {} System.out.println("Got:" + input + "\n"); return input; } public static void main(String[] args) throws InterruptedException { DeadlockTest dl = new DeadlockTest(); new Thread(() -> { dl.getFromBuffer(); }).start(); // Give new thread time to run. Thread.sleep(500); synchronized (dl) { String message = "Hello, world!"; System.out.println("Adding: " + message); dl.buffer.put(message); System.out.println("Added!\n"); System.out.println("Message: " + dl.input); } } } 
3
  • Well, yes, so take/pop/whatever must release the lock even if the queue is empty. Commented Oct 31, 2015 at 14:54
  • Usually, you use extra thread to execute blocking stuff and then read results using some event loop. Commented Oct 31, 2015 at 14:59
  • @MartinJames The lock object that take() and put() acquire and release is not the same one that the synchronized method uses. I do not have access to the lock internal to the blocking queue. If I did I could use it as the argument to a synchronized block in place of making the method synchronized. Commented Oct 31, 2015 at 15:24

1 Answer 1

3

Say we create a thread which runs a synchronized method. This method tries to take() from an empty blocking queue.

Sounds like bad design. It's usually a mistake to call any blocking methods from within a synchronized method or a synchronized statement.

If the two actions need to be atomic and run on separate threads, how can this be achieved without causing a deadlock?

Well, there's two possibilities:

In one case, the two threads are acting on different data. In that case, they should be using different locks, and they won't interfere with one another at all.

In the other case, the two threads are acting on the same data. In that case, they should lock the same lock, and one thread will have to wait for the other.


Maybe you misunderstand how a blocking queue works. If one thread is waiting to take() something from a blocking queue, that should never prevent another thread from calling put(). That would be the exact opposite of what you want.

What you want (and what you'll get from any of the blocking queue implementations in the Java standard library) is that the put() operation in the second thread will wake up the thread that's waiting to take() something from the queue.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the feedback James! Poor design is certainly the problem. That's why I am asking how I can achieve a similar effect with a different design. My understanding is that 'take()' does indeed release a lock when it blocks. However this is not the same lock object that the synchronized method uses. If I had access to the lock object internal to the blocking queue then I could use that as the argument to a synchronized block. That is probably also poor design though!
The primitive feature that you want is the wait()/notify() mechanism. Have a look at the produceSomething() and consumeSomething() methods in my answer to a different question. stackoverflow.com/questions/26590542/… The lock.wait() call is only allowed inside a synchronized(lock) statement. It temporarily unlocks the lock, it waits to be notified by the other thread, and then it re-locks the lock before returning.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.