What determines which of two competing threads acquires a lock? - java

When two threads try to acquire the lock of the same object what are the things that are considered to decide upon to which thread the lock should be handed over.

According to the Java documentation for notify():
Wakes up a single thread that is waiting on this object's monitor. If
any threads are waiting on this object, one of them is chosen to be
awakened. The choice is arbitrary and occurs at the discretion of the
implementation. A thread waits on an object's monitor by calling one
of the wait methods.
So if you use synchronized(obj){} you basically have no control on which thread will obtain the lock on obj, and you cannot make any assumption. It depends on the scheduler.
If you want fairness (that is, the next thread obtaining the lock is the first in the queue), have a look at ReentrantLock: it has a boolean flag to specify you want to enforce fairness.

According to Java Oracle Docs:
The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order.
If you are allowing fairness then FIFO (First-in-First-out) is used, otherwise it seems random (from my observations).

Related

Difference in internal storing between 'fair' and 'unfair' lock

The way Lock interface with class Reentrant(true) lock works is that it uses BlockingQueue to store Threads that want to acquire the lock. In that way thread that 'came first, go out first'-FIFO. All clear about that.
But where do 'unfair locks' go, or ReentrantLock(false). What is their internal implementation? How does OS decide which thread now to pick? And most importantly are now these threads also stored in a queue or where? (they must be somewhere)
The class ReentrantLock does not use a BlockingQueue. It uses a non-public subclass of AbstractQueuedSynchronizer behind the scenes.
The AbstractQueuedSynchronizer class, as its documentation states, maintains “a first-in-first-out (FIFO) wait queue”. This data structure is the same for fair and unfair locks. The unfairness doesn’t imply that the lock would change the order of enqueued waiting threads, as there would be no advantage in doing that.
The key difference is that an unfair lock allows a lock attempt to succeed immediately when the lock just has been released, even when there are other threads waiting for the lock for a longer time. In that scenario, the queue is not even involved for the overtaking thread. This is more efficient than adding the current thread to the queue and putting it into the wait state while removing the longest waiting thread from the queue and changing its state to “runnable”.
When the lock is not available by the time, a thread tries to acquire it, the thread will be added to the queue and at this point, there is no difference between fair and unfair locks for it (except that other threads may overtake it without getting enqueued). Since the order has not been specified for an unfair lock, it could use a LIFO data structure behind the scenes, but it’s obviously simpler to have just one implementation code for both.
For synchronized, on the other hand, which does not support fair acquisition, there are some JVM implementations using a LIFO structure. This may change from one version to another (or even with the same, as a side effect of some JVM options or environmental aspects).
Another interesting point in this regard, is that the parameterless tryLock() of the ReentrantLock implementation will be unfair, even when the lock is otherwise in fair mode. This demonstrates that being unfair is not a property of the waiting queue here, but the treatment of the arriving thread that makes a new lock attempt.
Even when this lock has been set to use a fair ordering policy, a call to tryLock() will immediately acquire the lock if it is available, whether or not other threads are currently waiting for the lock. This "barging" behavior can be useful in certain circumstances, even though it breaks fairness.

Understanding fair ReentrantLock in Java

ReentrantLock API doc says:
The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread.
Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock.
I am not able to understand points 2:
If one thread obtain lock multiple times in succession, then as per point 1, other threads will wait for longer and that does mean they will get the lock next time. Then how this does not affect (fairness of) thread scheduling? Thus, I feel fair lock is nothing but longest waiting time first thread scheduling.
I think they're just trying to separate the fairness logic side from the scheduling logic. Threads may be concurrent, but that doesn't mean they try to access Locks simultaneously. Thread priority requests are only 'hints' to the OS, and are never guaranteed the way they may be expected.
So, just because you have threads A and B, which may request a lock, which may even have identical behavior, one thread may execute, acquire the lock, release, re-acquire, before the other locks even request it:
A: Request Lock -> Release Lock -> Request Lock Again (Succeeds)
B: Request Lock (Denied)...
----------------------- Time --------------------------------->
Thread scheduling logic is decoupled from the Lock logic.
There are other scheduling issues too, the burden of which often falls on the software designer, see Starvation and Livelock

Using volatile variables and semaphores - Java

I'm starting with Threads, Semaphores, volatile variables, etc.
I wonder if when I'm using Semaphores it is necessary to define the variable as volatile, I mean:
Having 2 Threads, one increases and the other decreases the variable for example and obviously, before each access I have a mutex that controls at any time only one thread is "playing" with the variable.
It would be necessary to define as volatile?
From API doc of Semaphore:
Memory consistency effects: Actions in a thread prior to calling a
"release" method such as release() happen-before actions following a
successful "acquire" method such as acquire() in another thread.
So it is safe to read/write variables that are guarded by a semaphore. No need to declare them as volatile.
Semaphores should not be used in the place of synchronized because semaphores does not hold exclusive mutual lock even if it is initialized to one, like synchronized on some object. It is true that the semaphore initialized to one, allows only one thread at a time to access the object, which holds the permit. But the thread which holds the permit does not own it, any other thread could release that permit and acquire the permit. So, two threads could get access to the same object at the same time, and if both threads manipulates that object, there will be multi-threading issues like lost update, stale read etc.
In your example of having 2 threads, one increasing and one decreasing the same variable. mutual exclusion is sufficient and volatile declaration is not needed. here I assume mutual exclusion is achieved via synchronized and not by semaphores.
volatile is less strict than synchronized, you may want to use volatile when the operations performed are atomic (read or write). volatile should not be used when the performing read-update-write operation.
I wonder if when I'm using Semaphores it is necessary to define the
variable as volatile,
I dont think there is any such restriction. A mutex is a mutual exclusion semaphore, a special variant of a semaphore that only allows one locker at a time. It's equivalent to a normal counting semaphore with a count of one and the requirement that it can only be released by the same thread that locked it.
If we talk specifically for Semaphore in Java: A semaphore is a counter of permits and acquire is like decrement which waits rather than go below zero. It has no upper limit. As mentioned in CIP:
The implementation has no actual permit objects, and Semaphore does
not associate dispensed permits with threads, so a permit acquired in
one thread can be released from another thread. You can think of
acquire as consuming a permit and release as creating one; a
Semaphore is not limited to the number of permits it was created with.
For your scenario you can share a counter and make it volatile or better use AtomicInteger as they use CAS mechanism which performs really well under low contention.

notify() implementation in Oracle's jvm

I am under the impression that most people use only the jvm implementation given by the Oracle (originally from Sun microsystems). Correct me if I am wrong.
When I went through the API for notify(), it says :
Wakes up a single thread that is waiting on this object's monitor. If
any threads are waiting on this object, one of them is chosen to be
awakened. The choice is arbitrary and occurs at the discretion of
the implementation.
I would like to know in what order the waiting threads will be invoked when notify() is called in the Oracle's jvm.
You might wonder why I am not considering to use notifyAll() and just stop worrying. But why should I invoke all the waiting threads unnecessarily when I can do with invoking just one thread with notify()? Even if I use notifyAll(), I have no control which of the waiting threads will get the monitor.
Oracle should have documented how it is implemented in its own implementation right in the api link given above.
The order of execution with Threads is undefined.
If you write any code based on the assumption that you can predict the order of execution, it will run on a single machine at best. So how Oracle actually implemented it is - except for a research case - irrelevant, as it probably is implemented differently on the next machine and even on the next version of the Oracle JVM.
If you need a more fine-grain control, then you need to adjust your architecture and use the classes from the concurrent package in a proper way. Synchronized/wait/notify is just a very basic "brute-force" implementation of thread-synchronization with many pit-falls and restrictions.
You can rely only on what API says and API does not guarantee any specific order. If you need threads to wake up in a certain order use ReentrantLock in fair mode then this lock's Condition.signal() will wake the thread waiting for this Condition longest.
You can use ReentrantLock(boolean fair) with fairness flag in constructor. Conditions created from a such lock are also fair:
Creates an instance of ReentrantLock with the given fairness policy.
and
The ordering of lock reacquisition for threads returning from waiting
methods is the same as for threads initially acquiring the lock, which
is in the default case not specified, but for fair locks favors those
threads that have been waiting the longest.

What does it mean when a thread moves out of an object's monitor?

All I want to know is that when a thread enters out of a lock, does it means it "ends" or just that it has finished using that function or code which employed the use of the object whose monitor that particular thread is in?
Just that it has finished using that function or code which employed the use of the object. Such pieces of code are commonly known as critical section(s).
For your general understanding: methods run on threads. So it is possible that one method is being executed by multiple threads at the same time.
Imagine you want to make sure that a method, or part of it, can only be executed by one thread at a time. This is called a critical section.
A critical section in Java can be protected by a lock: implicitly via synchronized or explicitly via java.util.concurrent.locks.
Only one thread at a time can acquire the lock and entering the critical section requires that the lock be acquired first. At the end of the critical section the lock is released and the thread continues running but now without holding that lock.
A thread encountering a lock held by another thread (not necessarily for the same critical section) cannot proceed at that point and must wait. The thread, and other threads waiting on the same lock, will be notified when they can retry to acquire the lock. Again, only one thread will win and the process repeats (unless you have a deadlock for example).

Categories

Resources