ReentrantLock
什麼是ReentrantLock ?
ReentrantLock 中文翻譯過來就是可重入鎖,也就是同一個線程這個鎖是可以重複獲取的 ,synchronize關鍵字就是一個隱式的可重入鎖。
ReentrantLock 的實現原理
ReentrantLock 是實現了Lock接口使得它能夠作爲一個鎖被使用,同時他還有一個內部類同步器:Sync ,這個類繼承了AQS(AbstractQueuedSynchronizer) 類,然後實現AQS裏面的模板方法。
自己實現一個ReentrantLock
這裏我們來實現一個獨佔式的非公平鎖
代碼骨架搭建
要實現一個 ReentrantLock 首先的實現Lock接口,如下:
class MyReentrantLock implements Lock{
/**
* 加鎖方法
*/
@Override
public void lock() {
}
/**
* 中斷加鎖方法
* @throws InterruptedException
*/
@Override
public void lockInterruptibly() throws InterruptedException {
}
/**
* 嘗試加鎖
* @return
*/
@Override
public boolean tryLock() {
return false;
}
/**
* 在規定的時間內 嘗試加鎖
* @param time
* @param unit
* @return
* @throws InterruptedException
*/
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
/**
* 解鎖
*/
@Override
public void unlock() {
}
/**
* 線程條件 裏面的方法與Object裏面的 wait,notify,notifyAll 等方法類似
* @return
*/
@Override
public Condition newCondition() {
return null;
}
}
同時還需要有一個基於AQS的同步器
這裏我們在上面的類裏面寫一個內部類來繼承AQS 同時重寫下面 的三個方法
到這裏這個ReentrantLock 的骨架就已經搭建好了,下面就是實現部分
實現MySync同步器
/**
* 自定義的同步器,僅僅實現
*/
class MySync extends AbstractQueuedSynchronizer {
/**
* 嘗試獲取鎖
* @param acquire
* @return
*/
@Override
protected boolean tryAcquire(int acquire) {
// System.out.println("加鎖");
//1、獲取當前的線程
Thread thread = Thread.currentThread();
//2、獲取當前的線程狀態
int state = getState() ;
//3、判斷狀態是不是爲0,如果爲0那麼就是說明當前鎖還未被持有,很有可能有其他線程來獲取該鎖,那麼需要進行CAS操作來設置鎖的持有狀態
if(state==0 && compareAndSetState(0,acquire)){
//獲取鎖成功,將持有線程設置爲當前線程
setExclusiveOwnerThread(thread);
return true;
//由於是獨佔式鎖,那麼如果是當前線程持有的鎖那麼就只需要設置狀態+1就好了
//否則就獲取不成功
}else if(thread==getExclusiveOwnerThread()){
if(state+acquire>=0){
setState(state+acquire);
}
return true;
}
return false;
}
/**
* 嘗試釋放鎖
* @param release
* @return
*/
@Override
protected boolean tryRelease(int release) {
//釋放鎖的時候有兩點需要注意
// 1、釋放鎖的時候肯定是隻有持有鎖的線程才能來釋放鎖,所以不需要CAS操作
//2、釋放鎖的時候只有當狀態爲0 的時候纔算釋放完成
Thread thread = Thread.currentThread();
if(thread!=getExclusiveOwnerThread()){
throw new RuntimeException("非法操作");
}
int state = getState()-release;
boolean flag = false;
//如果狀態爲0
if(state==0){
//將持有線程置空
setExclusiveOwnerThread(null);
flag= true;
}
//設置線程狀態
setState(state);
//System.out.println("釋放");
return flag;
}
/**
* 是不是當前線程持有鎖
* @return
*/
@Override
protected boolean isHeldExclusively() {
return getExclusiveOwnerThread()==Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
}
lock 接口的實現
/**
* 加鎖方法
*/
@Override
public void lock() {
this.mySync.acquire(1);
}
/**
* 中斷加鎖方法
* @throws InterruptedException
*/
@Override
public void lockInterruptibly() throws InterruptedException {
this.mySync.acquireInterruptibly(1);
}
/**
* 嘗試加鎖
* @return
*/
@Override
public boolean tryLock() {
return this.mySync.tryAcquire(1);
}
/**
* 在規定的時間內 嘗試加鎖
* @param time
* @param unit
* @return
* @throws InterruptedException
*/
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return this.mySync.tryAcquireNanos(1, unit.toNanos(time));
}
/**
* 解鎖
*/
@Override
public void unlock() {
this.mySync.release(1);
}
/**
* 線程條件 裏面的方法與Object裏面的 wait,notify,notifyAll 等方法類似
* @return
*/
@Override
public Condition newCondition() {
return this.mySync.newCondition();
}
到這裏自定義的 ReentrantLock(獨佔式非公平鎖) 就實現完成了
下面我們看一下效果
測試
我們寫如下一段測試代碼來測試是否成功
其中ThreadFactory 是自定義的一個線程工廠,可以不用傳入
public class SelfLock {
public static void main(String[] args) {
final MyReentrantLock reentrantLock = new MyReentrantLock();
final List<Integer> list = new ArrayList<>();
final CountDownLatch countDownLatch = new CountDownLatch(100);
final AtomicReference<MyInteger> atomicI = new AtomicReference<MyInteger>(new MyInteger(4999));
for (int i = 0; i < 5000; i++) {
list.add(i);
}
final MyInteger j = new MyInteger(4999);
/*定義線程工廠 方便bug回溯追蹤*/
ThreadFactory customThreadfactory = new ThreadFactoryBuilder()
.setNamePrefix("測試-Thread").setDaemon(false)
.setPriority(Thread.MAX_PRIORITY).build();
/*keepAliveTile 線程最大空閒時間 */
ExecutorService executorService =
new ThreadPoolExecutor(100, 200, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10),customThreadfactory);
long b = System.currentTimeMillis();
for(int i=0;i<100 ;i++){
executorService.execute(() -> {
while (true) {
// int j = atomicInteger.getAndDecrement();
if (j.getIndex() >= 0) {
reentrantLock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"====》" +list.get(j.getIndex()));
j.setIndex(j.getIndex()-1);
reentrantLock.unlock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
countDownLatch.countDown();
}
);
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("耗時---->" + (System.currentTimeMillis() - b));
executorService.shutdown();
}
}
class MyInteger{
int index;
public MyInteger(int i){
this.index=i;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
測試效果:
測試成功,沒有發生線程不安全的情況