Java中的鎖
1.Lock接口
鎖是用來控制多個線程訪問共享資源的方式,一般來說,一個鎖能夠防止多個線程訪問共享資源。在lock接口出現之前,java程序依靠synchroized關鍵字實現鎖的功能,Loc接口擁有了獲取鎖與釋放鎖的可操作性,可中斷的獲取鎖。
package cn.smallmartial.concurrency;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author smallmartial
* @Date 2019/8/25
* @Email [email protected]
*/
public class LockUseCase {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try{
}finally {
lock.unlock();
}
}
}
在finally塊中釋放鎖,目的保證在獲取鎖之後,最終能夠被釋放
2.隊列同步器
隊列同步器是用來構建鎖和其他同步組件的基礎,它使用一個int成員變量表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。
同步器是實現鎖的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義。鎖是面向使用者的,它定義了使用者與鎖的交互接口,隱藏了實現細節;同步器面向的是鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態管理,線程的排隊,等待與喚醒等底層操作。鎖與同步器很好的隔離了使用者和實現者所關注的領域。
同步器主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步器的狀態:
- getState():獲取當前的同步狀態
- setState(int newState):設置當前同步器的狀態
- compareAndSetState(int expect,int update):使用CAS設置當前狀態,該方法能夠保證狀態設置的原子性。
同步器提供的模板方法分爲3類:
- 獨佔式獲取與釋放同步狀態
- 共享式獲取與釋放同步狀態
- 查詢同步隊列中等待線程情況
package cn.smallmartial.lock;
import sun.misc.Lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
import java.util.concurrent.locks.Condition;
/**
* @Author smallmartial
* @Date 2019/8/25
* @Email [email protected]
*/
public class Mutex extends Lock {
//靜態內部類 自定義同步器
private static class Sync extends AbstractQueuedLongSynchronizer{
//是否處於佔用狀態
@Override
protected boolean isHeldExclusively(){
return getState() == 1;
}
//當狀態爲0的時候獲取鎖 同步器重寫方法 獨佔式獲取同步狀態
public boolean tryAcquire(int acquires){
/**
* CAS的機制就相當於這種(非阻塞算法),CAS是由CPU硬件實現,所以執行相當快.
* CAS有三個操作參數:內存地址,期望值,要修改的新值,當期望值和內存當中的值進行比較不相等的時候,
* 表示內存中的值已經被別線程改動過,這時候失敗返回,當相等的時候,將內存中的值改爲新的值,並返回成功。
*/
if(compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//釋放鎖,將狀態設置爲0 同步器重寫方法 獨佔式釋放同步狀態
protected boolean tryRelease(int relaeases){
if (getState() == 0){
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
//返回一個Condition,每個Condition都包含一個condition對列
Condition newCondition(){
return new ConditionObject();
}
}
private final Sync sync = new Sync();
public void lock1(){
sync.acquire(1);
}
public boolean tryLock(){
return sync.tryAcquire(1);
}
public void unlock1(){
sync.release(1);
}
public Condition newCondition(){
return sync.newCondition();
}
public boolean isLocked(){
return sync.isHeldExclusively();
}
public boolean hasQueueTHreads(){
return sync.hasQueuedThreads();
}
public void lockInterruptibly() throws InterruptedException{
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException{
return sync.tryAcquireNanos(1,unit.toNanos(timeout));
}
public static void main(String[] args) {
}
}
3.隊列同步器的實現分析
3.1同步器
3.2獨佔式同步狀態獲取流程,acquire(int arg)方法調用流程。
3.3總結
在獲取同步狀態時,同步器維護一個同步隊列,獲取狀態失敗的線程都會被加入隊列並在隊列中進行自旋;移除隊列(停止自旋的條件)是前驅節點爲頭節點且成功獲取了同步狀態。在釋放鎖同步狀態時,同步器調用tryRelease(int arg)方法釋放同步狀態,然後喚醒頭節點的後繼節點。