2

In Java if a thread, t2, attempts to attain a lock, from synchronized, which is currently in use by another thread, t1, then t2 will switch from runnable to blocked. Correct? What about with ReentrantLocks?

If the thread t1 finishes using the lock, does t2 then automatically switch back to runnable or do you need to use notifyAll()? What about with ReentrantLock usage without a condition. If you aren't using a condition how do you inform the thread t2 that it should switch back to runnable? Is it ever wise, or even possible to use reentrant locks without a condition?

If this question has already been answered (I couldn't find it), I would be grateful if you would link it to me.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
erik p
  • 360
  • 3
  • 15

2 Answers2

4

It sounds like you're confusing the blocked and waiting states. Blocked means that the thread is trying to acquire the lock and can't so is stuck. Waiting means the thread is dormant; it's hanging out until it receives a notification, or until it otherwise comes back from waiting (timeout, if called with a timeout value, or spurious wakeup).

Once a lock becomes available the OS scheduler has to decide which blocked thread gets it. The thread it picks to get the lock becomes runnable.

So notify pertains to waiting threads, not blocked ones. A thread that has the lock but which has figured out it can't progress (it detects the condition it's waiting for isn't true) can call wait on that lock, releasing the lock and going dormant. You use notify to tell the scheduler to wake up any one thread that is waiting on the lock. Once the thread is woken up it has to reacquire the lock it previously released before it can exit the wait method.

The basic behavior of ReentrantLock is analogous to intrinsic locks, except that you can have multiple conditions with reentrant locks. Keep in mind ReentrantLock has its own separate methods to call (await and signal instead of wait and notify). You would use conditions with ReentrantLock when you want the threads to wait and get notified, with different conditions used so that threads will be waiting only on conditions relevant to them.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • I think he's just using the term "runnable" for "waiting". – Warren Dew Jan 03 '17 at 19:43
  • My confusion comes from the book I'm reading (Object-Orinted Design and Patterns by Cay S. Horstmann). In the book the following can be read [Screenshot](http://imgur.com/a/qYm36). Is this a simplification or is there something I'm missing? – erik p Jan 03 '17 at 21:00
  • @erikp seems like a simplification to me - refer to the [JavaDocs of Thread.State](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html) – Hulk Jan 04 '17 at 09:04
  • 1
    @erikp also see the relevant chapter of the JLS: [Chapter 17. Threads and Locks](https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html) - blocking is handled in 17.1, while waiting and notification are handled in 17.2 – Hulk Jan 04 '17 at 09:14
  • @erik: this diagram could be useful for showing the role of the scheduler. It does simplify things, i think it's not intended for you to to use it as a roadmap for transitioning from one state to another, though I see how you could get that idea. – Nathan Hughes Jan 04 '17 at 13:49
0

If a thread t2 attempts to synchronize on a lock that is currently in use by another thread t1 - for example by attempting to enter a synchronized block when t1 is already in a synchronized block on the same lock - then t2 will block, yes. This is also true for reentrant locks, including the ReentrantLock class; it should be noted that default locks are reentrant in Java (more on this later).

If t1 releases a default lock, such as by exiting the synchronized block, then t2 is unblocked; this is a feature of the language. However, if you are using a ReentrantLock, the thread holding the lock must explicitly call ReentrantLock.unlock() to release the lock, just as it must have called ReentrantLock.lock() to obtain the lock.

Note that "reentrant" refers to whether a single thread can "reenter" synchronized blocks, not to any interaction between threads. Reentrant locks can be locked again by threads that already hold the lock; nonreentrant locks cannot. Note that in Java, if a single thread obtains a reentrant lock more than once, it must release the lock the same number of times before other threads waiting for the lock are unblocked. For default locks, this happens naturally with nested synchronized blocks, possibly at different function call levels.

Warren Dew
  • 8,790
  • 3
  • 30
  • 44