Understand Java Locks from following 4 aspects:
-
Where you store “who will own the lock” information. You can store in class, instance or lock itself.
sychronized
store the lock information in current class or piece of itReentryLock
store the lock information in the Lock.Semaphore
store the lock information in the Lock itself. -
The rule of who can acquire lock:
Mutex
: only 1 thread can acquire the lockSemaphore
: only limited threads can acquire LockReentryLock
: a thread can multiple time acquire the lockRead-Write Lock
: read can share the resource, write isMutex
lock. -
What you gonna do when you can NOT acquire lock
tryLock
if you don’t get the lock, exit.tryLock(3, Time.SECONDS
) if you don’t get the lock, wait for 3 seconds and then exit.lock()
you block the thread and wait for the lock. -
Fairness - when a lock released, who can get lock
Fair
: JVM need to maintain a queue to make sure waiting threads FIFO - this is good but more resource expend, so JVM thinkFair
lock is always more expensive thanUnFair
, so default all the lock isUnFair
UnFair
: once the Lock released, anyone is possible to get the lock in the pool. From System Design perspective,UnFair
is always ‘cheaper’ thanFair
, but actually in real world, it is hard to tell - because if you implemented theSpin
lock like if you puttryLock
in while loop, then instead of the threadpark
, it will spin until lock get free, thatspin
process of CPU intense process.
Optimistic
vs Pessimistic
lock
Optimistic
lock is thought that when there are multi threads access same resource, we “assume” there should be no concurrent issue, so no need to lock - CAS
is implementation of Optimistic
Pessimistic
lock is thought that every time there is locking issue when multi threads access the resource, so you should exclusive
lock the resource every time - synchronized
is implementation of Pessimistic
.
So, when you have situation that Read more than Write, you should use Optimistic
lock, and if Write more than Read, then you should use Pessimistic
lock.
CAS
: Compute And Swap
CAS
is Optimistic
locking (compare with pessimistic
locking). It basically do 2 things when lock/unlock.
- Before it lock, it obtain atomic number in memory (V) and record the current value (A).
- When try to lock, it check the value of V is STILL equal to A? if yes, set V value to B, and return true - meaning lock successful, if not, then return false meaning lock failed. - Compute And Swap
Be aware that the Compute And Swap
is CPU level of atomic process (it is called CAH
instruct ) , in Java it use sun.misc.Unsafe
class, and underneath Java use C to provide lock.
But CAS
is not always thread-safe, it has potential issue called ABA
issue:
if Thread 1: T1
get the variable V, and the value of V is A, and Thread 2: T2
also get variable V and value of V is A, after while, T2
updated the V to value B, but for some reason, T2
updated V back to value A, in this case, when T1
compare the value of V, T1
thought there is no change, so T1
will overwrite T2
’s change.
AQS
: Abstract Queued Synchronizer
AQS
provided a Queue type data structure (State
and Node
- (vale, prevNode and nextNode ) ) that could be used in Lock mechanism. The Lock
, ReentryLock
, Semaphore
and all the Atomic
classes are based on the AQS
Framework.
here is the pseudo code for AQS
:
class AbstractQueuedSynchronizer {
volatile Node head;
volatile Node tail;
volatile int state;
}
class Node {
Node prev;
Node next;
Thread thread;
Node nextWaiter;
int waitStatus;
}
The AQS
maintain the head
and tail
, when the lock is free, the head
thread will be wake up(unpark), and if a thread failed to acquire lock, it will be set as tail
.
Each Node
Object has prev
and next
, which if Fair
lock, then there is another method called hasQueuedPredecessors()
, if implement this method while try to lock, then it will be Fair
lock.