在Java中通常實(shí)現(xiàn)鎖有兩種方式宾添,一種是synchronized關(guān)鍵字船惨,另一種是Lock。二者其實(shí)并沒(méi)有什么必然聯(lián)系辞槐,但是各有各的特點(diǎn)掷漱,在使用中可以進(jìn)行取舍的使用。首先我們先對(duì)比下兩者榄檬。
實(shí)現(xiàn):#####
首先最大的不同:synchronized是基于JVM層面實(shí)現(xiàn)的卜范,而Lock是基于JDK層面實(shí)現(xiàn)的。曾經(jīng)反復(fù)的找過(guò)synchronized的實(shí)現(xiàn)鹿榜,可惜最終無(wú)果海雪。但Lock卻是基于JDK實(shí)現(xiàn)的锦爵,我們可以通過(guò)閱讀JDK的源碼來(lái)理解Lock的實(shí)現(xiàn)。
使用:#####
對(duì)于使用者的直觀體驗(yàn)上Lock是比較復(fù)雜的奥裸,需要lock和realse险掀,如果忘記釋放鎖就會(huì)產(chǎn)生死鎖的問(wèn)題,所以湾宙,通常需要在finally中進(jìn)行鎖的釋放樟氢。但是synchronized的使用十分簡(jiǎn)單,只需要對(duì)自己的方法或者關(guān)注的同步對(duì)象或類使用synchronized關(guān)鍵字即可侠鳄。但是對(duì)于鎖的粒度控制比較粗埠啃,同時(shí)對(duì)于實(shí)現(xiàn)一些鎖的狀態(tài)的轉(zhuǎn)移比較困難。例如:
特點(diǎn):#####
tips | synchronized | Lock |
---|---|---|
鎖獲取超時(shí) | 不支持 | 支持 |
獲取鎖響應(yīng)中斷 | 不支持 | 支持 |
優(yōu)化:#####
在JDK1.5之后synchronized引入了偏向鎖伟恶,輕量級(jí)鎖和重量級(jí)鎖碴开,從而大大的提高了synchronized的性能,同時(shí)對(duì)于synchronized的優(yōu)化也在繼續(xù)進(jìn)行博秫。期待有一天能更簡(jiǎn)單的使用java的鎖潦牛。
在以前不了解Lock的時(shí)候,感覺(jué)Lock使用實(shí)在是太復(fù)雜挡育,但是了解了它的實(shí)現(xiàn)之后就被深深吸引了巴碗。
Lock的實(shí)現(xiàn)主要有ReentrantLock、ReadLock和WriteLock即寒,后兩者接觸的不多良价,所以簡(jiǎn)單分析一下ReentrantLock的實(shí)現(xiàn)和運(yùn)行機(jī)制。
ReentrantLock類在java.util.concurrent.locks包中蒿叠,它的上一級(jí)的包java.util.concurrent主要是常用的并發(fā)控制類.
下面是ReentrantLock的UML圖,從圖中可以看出蚣常,ReentrantLock實(shí)現(xiàn)Lock接口市咽,在ReentrantLock中引用了AbstractQueuedSynchronizer的子類,所有的同步操作都是依靠AbstractQueuedSynchronizer(隊(duì)列同步器)實(shí)現(xiàn)抵蚊。
研究一個(gè)類施绎,需要從一個(gè)類的靜態(tài)域,靜態(tài)類贞绳,靜態(tài)方法和成員變量開(kāi)始谷醉。
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes this lock instance from a stream.
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
從上面的代碼可以看出來(lái)首先ReentrantLock是可序列化的,其次是ReentrantLock里有一個(gè)對(duì)AbstractQueuedSynchronizer的引用冈闭。
看完了成員變量和靜態(tài)域俱尼,我們需要了解下構(gòu)造方法:
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
從上面代碼可以看出,ReentrantLock支持兩種鎖模式萎攒,公平鎖和非公平鎖遇八。默認(rèn)的實(shí)現(xiàn)是非公平的矛绘。公平和非公平鎖的實(shí)現(xiàn)如下:
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
AbstractQueuedSynchronizer 是一個(gè)抽象類,所以在使用這個(gè)同步器的時(shí)候刃永,需要通過(guò)自己實(shí)現(xiàn)預(yù)期的邏輯货矮,Sync、FairSync和NonfairSync都是ReentrantLock為了實(shí)現(xiàn)自己的需求而實(shí)現(xiàn)的內(nèi)部類斯够,之所以做成內(nèi)部類囚玫,我認(rèn)為是只在ReentrantLock使用上述幾個(gè)類,在外部沒(méi)有使用到读规。
我們著重關(guān)注默認(rèn)的非公平鎖的實(shí)現(xiàn):
在ReentrantLock調(diào)用lock()的時(shí)候抓督,調(diào)用的是下面的代碼:
/**
* Acquires the lock.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
* at which time the lock hold count is set to one.
*/
public void lock() {
sync.lock();
}
sync的實(shí)現(xiàn)是NonfairSync,所以調(diào)用的是NonfairSync的lock方法:
/**
* Sync object for non-fair locks
* tips:調(diào)用Lock的時(shí)候掖桦,嘗試獲取鎖本昏,這里采用的CAS去嘗試獲取鎖,如果獲取鎖成功
* 那么枪汪,當(dāng)前線程獲取到鎖涌穆,如果失敗,調(diào)用acquire處理雀久。
*
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
接下來(lái)看看compareAndSetState方法是怎么進(jìn)行鎖的獲取操作的:
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a <tt>volatile</tt> read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that the actual
* value was not equal to the expected value.
*
* tips: 1.compareAndSetState的實(shí)現(xiàn)主要是通過(guò)Unsafe類實(shí)現(xiàn)的宿稀。
* 2.之所以命名為Unsafe,是因?yàn)檫@個(gè)類對(duì)于JVM來(lái)說(shuō)是不安全的赖捌,我們平時(shí)也是使用不了這個(gè)類的祝沸。
* 3.Unsafe類內(nèi)封裝了一些可以直接操作指定內(nèi)存位置的接口,是不是感覺(jué)和C有點(diǎn)像了越庇?
* 4.Unsafe類封裝了CAS操作罩锐,來(lái)達(dá)到樂(lè)觀的鎖的爭(zhēng)搶的效果
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
主要的說(shuō)明都在方法的注釋中,接下來(lái)簡(jiǎn)單的看一下compareAndSwapInt的實(shí)現(xiàn):
/**
* Atomically update Java variable to <tt>x</tt> if it is currently
* holding <tt>expected</tt>.
* @return <tt>true</tt> if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
一個(gè)native方法卤唉,沮喪.....但是從注釋看意思是涩惑,以CAS的方式將制定字段設(shè)置為指定的值。同時(shí)我們也明白了這個(gè)方法可能是用java實(shí)現(xiàn)不了桑驱,只能依賴JVm底層的C代碼實(shí)現(xiàn)竭恬。下面看看操作的stateOffset:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
//這個(gè)方法很有意思,主要的意思是獲取AbstractQueuedSynchronizer的state成員的偏移量
//通過(guò)這個(gè)偏移量來(lái)更新state成員熬的,另外state是volatile的來(lái)保證可見(jiàn)性痊硕。
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
stateOffset 是AbstractQueuedSynchronizer內(nèi)部定義的一個(gè)狀態(tài)量,AbstractQueuedSynchronizer是線程的競(jìng)態(tài)條件押框,所以只要某一個(gè)線程CAS改變狀態(tài)成功岔绸,同時(shí)在沒(méi)有釋放的情況下,其他線程必然失敗(對(duì)于Unsafe類還不是很熟悉亭螟,后面還需要系統(tǒng)的學(xué)習(xí))挡鞍。
對(duì)于競(jìng)爭(zhēng)成功的線程會(huì)調(diào)用setExclusiveOwnerThread方法:
/**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread;
/**
* Sets the thread that currently owns exclusive access. A
* <tt>null</tt> argument indicates that no thread owns access.
* This method does not otherwise impose any synchronization or
* <tt>volatile</tt> field accesses.
*/
protected final void setExclusiveOwnerThread(Thread t) {
exclusiveOwnerThread = t;
}
這個(gè)實(shí)現(xiàn)是比較簡(jiǎn)單的,只是獲取當(dāng)前線程的引用预烙,令A(yù)bstractOwnableSynchronizer中的exclusiveOwnerThread引用到當(dāng)前線程墨微。
對(duì)于競(jìng)爭(zhēng)失敗的線程,會(huì)調(diào)用acquire方法扁掸,這個(gè)方法也是ReentrantLock設(shè)計(jì)的精華之處:
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
* tips:此處主要是處理沒(méi)有獲取到鎖的線程
* tryAcquire:重新進(jìn)行一次鎖獲取和進(jìn)行鎖重入的處理翘县。
* addWaiter:將線程添加到等待隊(duì)列中。
* acquireQueued:自旋獲取鎖谴分。
* selfInterrupt:中斷線程锈麸。
* 三個(gè)條件的關(guān)系為and,如果 acquireQueued返回true,那么線程被中斷selfInterrupt會(huì)中斷線程
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AbstractQueuedSynchronizer為抽象方法牺蹄,調(diào)用tryAcquire時(shí)忘伞,調(diào)用的為NonfairSync的tryAcquire。
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
nonfairTryAcquire方法主要是做重入鎖的實(shí)現(xiàn)沙兰,synchronized本身支持鎖的重入氓奈,而ReentrantLock則是通過(guò)此處實(shí)現(xiàn)。在鎖狀態(tài)為0時(shí)鼎天,重新嘗試獲取鎖舀奶。如果已經(jīng)被占用,那么做一次是否當(dāng)前線程為占用鎖的線程的判斷斋射,如果是一樣的那么進(jìn)行計(jì)數(shù)育勺,當(dāng)然在鎖的relase過(guò)程中會(huì)進(jìn)行遞減,保證鎖的正常釋放罗岖。
如果沒(méi)有重新獲取到鎖或者鎖的占用線程和當(dāng)前線程是一個(gè)線程涧至,方法返回false。那么把線程添加到等待隊(duì)列中桑包,調(diào)用addWaiter:
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
這里主要是用當(dāng)前線程構(gòu)建一個(gè)Node的等待隊(duì)列雙向鏈表化借,這里addWaiter中和enq中的部分邏輯是重復(fù)的,個(gè)人感覺(jué)可能是如果能一次成功就避免了enq中的死循環(huán)捡多。因?yàn)閠ail節(jié)點(diǎn)是volatile的同時(shí)node也是不會(huì)發(fā)生競(jìng)爭(zhēng)的所以node.prev = pred;是安全的。但是tail的next是不斷競(jìng)爭(zhēng)的铐炫,所以利用compareAndSetTail保證操作的串行化垒手。接下來(lái)調(diào)用acquireQueued方法:
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
此處是做Node節(jié)點(diǎn)線程的自旋過(guò)程,自旋過(guò)程主要檢查當(dāng)前節(jié)點(diǎn)是不是head節(jié)點(diǎn)的next節(jié)點(diǎn)倒信,如果是科贬,則嘗試獲取鎖,如果獲取成功,那么釋放當(dāng)前節(jié)點(diǎn)榜掌,同時(shí)返回优妙。至此一個(gè)非公平鎖的鎖獲取過(guò)程結(jié)束。
如果這里一直不斷的循環(huán)檢查憎账,其實(shí)是很耗費(fèi)性能的套硼,JDK的實(shí)現(xiàn)肯定不會(huì)這么“弱智”,所以有了shouldParkAfterFailedAcquire和parkAndCheckInterrupt胞皱,這兩個(gè)方法就實(shí)現(xiàn)了線程的等待從而避免無(wú)限的輪詢:
/**
* Checks and updates status for a node that failed to acquire.
* Returns true if thread should block. This is the main signal
* control in all acquire loops. Requires that pred == node.prev
*
* @param pred node's predecessor holding status
* @param node the node
* @return {@code true} if thread should block
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
首先邪意,檢查一下當(dāng)前Node的前置節(jié)點(diǎn)pred是否是SIGNAL,如果是SIGNAL反砌,那么證明前置Node的線程已經(jīng)Park了雾鬼,如果waitStatus>0,那么當(dāng)前節(jié)點(diǎn)已經(jīng)Concel或者中斷。那么不斷調(diào)整當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)宴树,將已經(jīng)Concel的和已經(jīng)中斷的線程移除隊(duì)列策菜。如果waitStatus<0,那么設(shè)置waitStatus為SIGNAL,因?yàn)檎{(diào)用shouldParkAfterFailedAcquire的方法為死循環(huán)調(diào)用酒贬,所以終將返回true又憨。接下來(lái)看parkAndCheckInterrupt方法,當(dāng)shouldParkAfterFailedAcquire返回True的時(shí)候執(zhí)行parkAndCheckInterrupt方法:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
此方法比較簡(jiǎn)單同衣,其實(shí)就是使當(dāng)前的線程park竟块,即暫停了線程的輪詢。當(dāng)Unlock時(shí)會(huì)做后續(xù)節(jié)點(diǎn)的Unpark喚醒線程繼續(xù)爭(zhēng)搶鎖耐齐。
接下來(lái)看一下鎖的釋放過(guò)程浪秘,鎖釋放主要是通過(guò)unlock方法實(shí)現(xiàn):
/**
* Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then the hold
* count is decremented. If the hold count is now zero then the lock
* is released. If the current thread is not the holder of this
* lock then {@link IllegalMonitorStateException} is thrown.
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() {
sync.release(1);
}
主要是調(diào)用AbstractQueuedSynchronizer同步器的release方法:
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease方法為ReentrantLock中的Sync的tryRelease方法:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
tryRelease方法主要是做了一個(gè)釋放鎖的過(guò)程,將同步狀態(tài)state -1埠况,直到減到0為止耸携,這主要是兼容重入鎖設(shè)計(jì)的,同時(shí)setExclusiveOwnerThread(null)清除當(dāng)前占用的線程辕翰。這些head節(jié)點(diǎn)后的線程和新進(jìn)的線程就可以開(kāi)始爭(zhēng)搶夺衍。這里需要注意的是對(duì)于同步隊(duì)列中的線程來(lái)說(shuō)在setState(c)瓤鼻,且c為0的時(shí)候酵紫,同步隊(duì)列中的線程是沒(méi)有競(jìng)爭(zhēng)鎖的,因?yàn)榫€程被park了還沒(méi)有喚醒惠啄。但是此時(shí)對(duì)于新進(jìn)入的線程是有機(jī)會(huì)獲取到鎖的壁榕。
下面代碼是進(jìn)行線程的喚醒:
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
因?yàn)樵趕etState(c)釋放了鎖之后矛紫,是沒(méi)有線程競(jìng)爭(zhēng)的,所以head是當(dāng)前的head節(jié)點(diǎn)牌里,先檢查當(dāng)前的Node是否合法颊咬,如果合法則unpark it。開(kāi)始鎖的獲取。就回到了上面的for循環(huán)執(zhí)行獲取鎖邏輯:
至此鎖的釋放就結(jié)束了喳篇,可以看到ReentrantLock是一個(gè)不斷的循環(huán)的狀態(tài)模型敞临,里面有很多東西值得我們學(xué)習(xí)和思考。
ReentrantLock具有公平和非公平兩種模式麸澜,也各有優(yōu)缺點(diǎn):
公平鎖是嚴(yán)格的以FIFO的方式進(jìn)行鎖的競(jìng)爭(zhēng)挺尿,但是非公平鎖是無(wú)序的鎖競(jìng)爭(zhēng),剛釋放鎖的線程很大程度上能比較快的獲取到鎖痰憎,隊(duì)列中的線程只能等待票髓,所以非公平鎖可能會(huì)有“饑餓”的問(wèn)題。但是重復(fù)的鎖獲取能減小線程之間的切換铣耘,而公平鎖則是嚴(yán)格的線程切換洽沟,這樣對(duì)操作系統(tǒng)的影響是比較大的,所以非公平鎖的吞吐量是大于公平鎖的蜗细,這也是為什么JDK將非公平鎖作為默認(rèn)的實(shí)現(xiàn)裆操。
最后:#####
關(guān)于并發(fā)和Lock還有很多的點(diǎn)還是比較模糊,我也會(huì)繼續(xù)學(xué)習(xí)炉媒,繼續(xù)總結(jié)踪区,如果文章中有什么問(wèn)題,還請(qǐng)各位看客及時(shí)指出吊骤,共同學(xué)習(xí)缎岗。