欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

Java多線程(二)——ReentrantLock源碼分析-創(chuàng)新互聯(lián)

ReentrantLock源碼解析

ReentrantLock 是可重入的互斥鎖,雖然具有與 Synchronized 相同的功能,但比 Synchronized 更加靈活。 ReentrantLock 底層基于 AQS(AbstractQueuedSynchronizer)實(shí)現(xiàn)。

創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括耀州網(wǎng)站建設(shè)、耀州網(wǎng)站制作、耀州網(wǎng)頁(yè)制作以及耀州網(wǎng)絡(luò)營(yíng)銷策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,耀州網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到耀州省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

Reentrant 實(shí)現(xiàn)了 Lock 接口,其是 Java 對(duì)鎖操作行為的統(tǒng)一規(guī)范,Lock接口定義如下:

public interface Lock{//獲取鎖
    void lock();
    //獲取鎖-可以響應(yīng)中斷
    void lockInterruptibly() throws InterruptedException;
    //嘗試獲取一次鎖
    boolean tryLock();
    //返回獲取鎖是否成功狀態(tài) - 響應(yīng)中斷
    boolean tryLock(long time,TimeUnit unit) throws InterrptedException;
    //釋放鎖
    void unlock();
    //創(chuàng)建條件變量
    Condition newCondition();
}
1. ReentrantLock的使用

使用 ReentrantLock 的 lock() 方法進(jìn)行鎖的獲取,即上鎖。使用 unlock() 方法進(jìn)行解鎖。

public class ReentrantLockDemo1 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();
        lock.lock();
        try{	//臨界區(qū)代碼
        }finally {//為避免臨界區(qū)代碼出現(xiàn)異常,導(dǎo)致鎖無(wú)法釋放,必須在finally中加上釋放鎖的語(yǔ)句
            lock.unlock();
        }
    }
}

ReentrantLock 也是可重入鎖:

public class ReentrantLockDemo1 {ReentrantLock lock = new ReentrantLock();

    Thread t1 = new Thread() {@Override
        public void run() {lock.lock();
            try {//獲取鎖之后,在m1中進(jìn)行鎖的重入
                m1();
            } finally {lock.unlock();
            }
        }

        private void m1() {lock.lock();
            try {//臨界區(qū)
            } finally {lock.unlock();
            }
        }
    };

    public static void main(String[] args) {ReentrantLockDemo1 demo = new ReentrantLockDemo1();
        demo.t1.start();
    }

}

默認(rèn)情況下,通過(guò)構(gòu)造方法new ReentrantLock()獲取的鎖為非公平鎖。

public class ReentrantLock{...
    public ReentrantLock() {sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
    }
	...
}

為觀察公平與非公平的區(qū)別,我們嘗試如下程序:

public class ReentrantLockDemo1 {//啟用公平鎖
    ReentrantLock lock = new ReentrantLock(true);

    Runnable run = new Runnable() {@Override
        public void run() {for (int i = 100; i >0; i--) {lock.lock();
                try {System.out.println(Thread.currentThread().getName() + " got the lock");
                } finally {lock.unlock();
                }
            }
        }
    };

    public static void main(String[] args) {ReentrantLockDemo1 demo = new ReentrantLockDemo1();
        Thread t1 = new Thread(demo.run,"t1");
        Thread t2 = new Thread(demo.run,"t2");
        t1.start();
        t2.start();
    }

}

使用公平鎖時(shí),上述程序運(yùn)行結(jié)果:

t1 got the lock
t2 got the lock
t1 got the lock
t2 got the lock
t1 got the lock
t2 got the lock
t1 got the lock
...
  1. t1獲取到鎖的時(shí)候,t2發(fā)現(xiàn)有人持有鎖,進(jìn)入隊(duì)列中排隊(duì)
  2. t1釋放鎖的時(shí)候,喚醒t2,t2程序繼續(xù)運(yùn)行
  3. t1開始acquire()方法,而t2已經(jīng)在判斷自己是否為隊(duì)列頭,并嘗試獲取鎖
  4. 正常情況t2會(huì)比t1更先獲取到鎖資源
  5. t1發(fā)現(xiàn)t2持有鎖,t1進(jìn)入隊(duì)列中排隊(duì)
  6. 循環(huán)上述情況導(dǎo)致運(yùn)行結(jié)果如上。

使用非公平鎖時(shí),上述程序運(yùn)行結(jié)果:

...
t1 got the lock
t1 got the lock
t1 got the lock
t1 got the lock
t2 got the lock
t2 got the lock
t2 got the lock
...
  1. t1獲取鎖的時(shí)候,t2在隊(duì)列中排隊(duì)
  2. t1釋放鎖的時(shí)候,喚醒t2,t2程序繼續(xù)運(yùn)行
  3. t2還在嘗試獲取前驅(qū),并tryAquire()時(shí),t1已經(jīng)在casState()了
  4. 正常情況t1會(huì)比t2更快獲取到所資源
  5. t2發(fā)現(xiàn)t1有鎖,t2進(jìn)入隊(duì)列中排隊(duì)
  6. 循環(huán)上述情況導(dǎo)致運(yùn)行結(jié)果如上。
2. ReentrantLock的實(shí)現(xiàn)方式 2.1 非公平鎖與公平鎖的上鎖實(shí)現(xiàn) step1: lock() —— 上鎖入口

ReentrantLock 首先調(diào)用 lock 方法嘗試獲取鎖資源。

public void lock() {sync.lock();
}

開啟公平鎖時(shí),sync 對(duì)象為 FairSync 實(shí)例,開啟非公平鎖時(shí),sync 對(duì)象為 NonFairSync 對(duì)象。

public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

觀察公平與非公平的 lock() 實(shí)現(xiàn)方式的不同。我們發(fā)現(xiàn):

公平鎖在獲取鎖的時(shí)候,會(huì)直接進(jìn)行 acquire() ,而非公平鎖則是直接嘗試 CAS 去更新鎖資源的 state 變量,更新成功則獲取到鎖資源,如果獲取不到,才會(huì)進(jìn)入 acquire()。這里涉及到 AQS 的 state 與 雙向鏈表數(shù)據(jù)結(jié)構(gòu),可以在AQS專題學(xué)習(xí)。

CAS + volatile 實(shí)現(xiàn)線程安全地更新變量

CAS(CompareAndSwap):在Java中,使用Unsafe類的compareAndSet()方法可以通過(guò)底層的 lock cmpxchg 指令實(shí)現(xiàn)原子性操作。

volatile :保證了線程間的變量一致性,即可見性。

CAS + Volatile:多線程場(chǎng)景中,某個(gè)個(gè)線程通過(guò) CAS 將 volatile 修飾的變量更新成功后,所有線程在使用該變量時(shí),都可見該變量的最新值。從而保證,在多線程場(chǎng)景下,對(duì)該變量的修改,不會(huì)引起線程安全問(wèn)題。

static final class FairSync extends Sync {...
        final void lock() {//直接進(jìn)入acquire
            acquire(1);
		}
}

static final class NonfairSync extends Sync {   	...
        final void lock() {//先嘗試更新AQS的State競(jìng)爭(zhēng)鎖
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //直接獲取鎖失敗時(shí),才會(huì)進(jìn)入acquire
                acquire(1);
        }
}

compareAndSetState():調(diào)用 Unsafe類提供的native層的原子性 CAS 操作。修改 AQS 中的 state 變量。

protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

setExclusiveOwnerThread():在 AQS 中將當(dāng)前線程設(shè)置為鎖的持有者

protected final void setExclusiveOwnerThread(Thread thread) {exclusiveOwnerThread = thread;
}
step2: accquire() —— 模板方法

FairSync 與 NonFairSync 都是 AQS 的子類,acquire() 是 AQS 向子類提供的模板方法。其中 tryAcquire() 方法需要子類重寫實(shí)現(xiàn)。

public final void acquire(int arg) {//先根據(jù)公平與非公平不同的方式,進(jìn)行嘗試獲取鎖
        if (!tryAcquire(arg) &&
            //如果獲取失敗,則排隊(duì)等待
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}
step3: tryAquire() 的不同實(shí)現(xiàn)

tryAquire()方法需要子類重寫實(shí)現(xiàn),在 AQS 中,該方法僅拋出一個(gè)異常:

protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();
}

先看到公平鎖對(duì) tryAquire() 的實(shí)現(xiàn)。公平鎖的 tryAquire() 主要做了:

  1. 如果自己持有鎖,則進(jìn)行鎖的重入
  2. 如果鎖空閑,先看是否有人排隊(duì)(非公平會(huì)直接CAS獲取鎖)
  3. 如果沒(méi)有人排隊(duì),則CAS嘗試獲取所資源
protected final boolean tryAcquire(int acquires) {   final Thread current = Thread.currentThread();
       //查看是否有人持有鎖
       int c = getState();
       if (c == 0) {//如果沒(méi)有人持有鎖
           //查看是否有人排隊(duì),如果沒(méi)人排隊(duì)則嘗試CAS獲取鎖
           if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {//獲取鎖成功,將AQS持有鎖的線程設(shè)置為本線程
                setExclusiveOwnerThread(current);
                return true;
           }
        }
        else if (current == getExclusiveOwnerThread()) {//鎖的重入
            int nextc = c + acquires;
            if (nextc< 0)
                throw new Error("Maximum lock count exceeded");
            //這里可以直接設(shè)置,同一個(gè)線程,不會(huì)有線程安全。
            //state>0表示有人持有鎖,state的具體數(shù)值表示鎖的重入次數(shù)
            setState(nextc);
            return true;
        }
        return false;
}

而非公平鎖則不同,非公平鎖的 tryAquire() 主要做了:

  1. 如果自己持有鎖,則進(jìn)行鎖的重入
  2. 如果鎖空閑,直接CAS嘗試獲取鎖(公平鎖會(huì)先看是否有人排隊(duì))
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//如果鎖資源空閑,直接CAS嘗試獲取鎖
            if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {//鎖的重入
            int nextc = c + acquires;
            //重入次數(shù)過(guò)多,int類型會(huì)overflow變成負(fù)數(shù)
            if (nextc< 0) // overflow
               throw new Error("Maximum lock count exceeded");
            //鎖重入,直接設(shè)置新的值,不會(huì)有線程安全問(wèn)題
            setState(nextc);
            return true;
        }
        return false;
}
step4. 入隊(duì)并阻塞線程,由 AQS 實(shí)現(xiàn)

在acquire()模板方法中,如果tryAquire()沒(méi)有獲取到鎖,將會(huì)準(zhǔn)備在 AQS 中排隊(duì)。主要工作:

  1. 將當(dāng)前線程包裝在 AQS 的 Node結(jié)構(gòu) 中
  2. 插入 AQS 的雙向隊(duì)列的隊(duì)尾
public final void acquire(int arg) {if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

private Node addWaiter(Node mode) {//將要入隊(duì)的線程封裝到 AQS 的 Node結(jié)構(gòu) 中
    Node node = new Node(Thread.currentThread(), mode);
    //獲取隊(duì)尾元素
    Node pred = tail;
    //如果隊(duì)尾元素不為空,則跟在隊(duì)尾元素之后
    if (pred != null) {node.prev = pred;
        //通過(guò)CAS保證線程安全地入隊(duì)
        if (compareAndSetTail(pred, node)) {pred.next = node;
            return node;
       }
    }
    //如果入隊(duì)失敗,通過(guò)enq來(lái)循環(huán)CAS,自旋嘗試入隊(duì)
    enq(node);
    return node;
}

private Node enq(final Node node) {for (;;) {Node t = tail;
        //如果隊(duì)尾為空,說(shuō)明隊(duì)中沒(méi)有元素,連head都沒(méi)有
        if (t == null) {// Must initialize
            //cas 使隊(duì)頭隊(duì)尾指針指向空Node
            //head->Node()<- tail
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {//線程安全地嘗試插入隊(duì)尾
            node.prev = t;
            if (compareAndSetTail(t, node)) {t.next = node;
                return t;
            }
        }
    }
}

通過(guò)addWaiter(),不論公平鎖還是非公平鎖,都將當(dāng)前線程包裝在Node結(jié)構(gòu)中,并插入到 AQS 的雙向鏈表的隊(duì)列末尾。

而后在 acquireQueued() 中,視情況再次獲取鎖,或者直接嘗試阻塞線程:

  1. 如果該線程所在的Node在隊(duì)列中處于隊(duì)頭,可以tryAquire()再次嘗試獲取鎖資源,公平鎖與非公平鎖都將直接 CAS 爭(zhēng)取。(因?yàn)榧词故枪芥i,該線程也處在隊(duì)頭,hasQueuedPredecessors()判斷為真)
  2. 如果獲取失敗,將做阻塞前的準(zhǔn)備
  3. 阻塞準(zhǔn)備完成后阻塞線程。
final boolean acquireQueued(final Node node, int arg) {boolean failed = true;
        try {boolean interrupted = false;
            for (;;) {//獲取前驅(qū)節(jié)點(diǎn)
                final Node p = node.predecessor();
                //如果前驅(qū)是head(head是空節(jié)點(diǎn)),說(shuō)明當(dāng)前Node排在隊(duì)頭,嘗試獲取所資源
                if (p == head && tryAcquire(arg)) {//如果獲取資源成功,則不阻塞當(dāng)前線程,而是return回去,繼續(xù)程序的執(zhí)行
                    //同時(shí)將包裝當(dāng)前的Node清空,并變?yōu)樾碌膆ead
                    setHead(node);
                    //將原來(lái)的頭清空應(yīng)用,等待GC回收
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //如果爭(zhēng)鎖失敗,將會(huì)準(zhǔn)備阻塞,如果本次準(zhǔn)備失敗,將會(huì)再循環(huán)一次到這里,準(zhǔn)備成功即可阻塞。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {...
        }
}

shouldParkAfterFailedAcquire()做了阻塞線程前的判斷,主要工作是:

  1. 如果前驅(qū)結(jié)點(diǎn)是正常的(waitStatus< 0),則當(dāng)前線程可以阻塞。
  2. 如果前驅(qū)結(jié)點(diǎn)的waitStatus==0,說(shuō)明剛被初始化,還沒(méi)被使用,CAS嘗試將其更新為waitStatus = -1;
  3. 如果前驅(qū)結(jié)點(diǎn)的waitStatus>0,則該前驅(qū)結(jié)點(diǎn)是要被廢棄的,更新鏈表結(jié)構(gòu),拋棄廢棄的前驅(qū)結(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)//waitStatus = -1
            return true;//前驅(qū)結(jié)點(diǎn)正常,當(dāng)前線程可以阻塞
        if (ws >0) {//waitStatus = CANCELLED = 1
            do {//更新前驅(qū)節(jié)點(diǎn),將node的前驅(qū)引用指向更前一個(gè)
                //pred = pred.prev;
                //node.prev = pred;
                node.prev = pred = pred.prev;
            } while (pred.waitStatus >0);
            //最后將可用的前驅(qū)結(jié)點(diǎn)指向node自己,從而拋棄中間若干個(gè)廢棄的節(jié)點(diǎn)
            pred.next = node;
        } else {//如果node的waitStatus<0 但不是-1,只需要都統(tǒng)一更新為-1即可。
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
}

準(zhǔn)備工作完成,就可以進(jìn)入線程阻塞,parkAndCheckInterrupt()方法通過(guò)Unsafe類實(shí)現(xiàn)線程的阻塞。

private final boolean parkAndCheckInterrupt() {LockSupport.park(this);
        return Thread.interrupted();
}

//LockSupport:
public static void park(Object blocker) {Thread t = Thread.currentThread();
    	//設(shè)置寫屏障 write barrier
        setBlocker(t, blocker);
    	//通過(guò)UNSAFE類的park()native方法進(jìn)行線程的阻塞。
        UNSAFE.park(false, 0L);
    	//設(shè)置寫屏障 write barrier
        setBlocker(t, null);
}
2.2 公平鎖與非公平鎖的解鎖實(shí)現(xiàn)

不論是公平鎖還是非公平鎖,解鎖的實(shí)現(xiàn)是一致的:

  1. 每次解鎖,都對(duì)state值減1
  2. 如果state的值變?yōu)榱?,說(shuō)明即使重入的鎖,也都完全退出
  3. 將 AQS 對(duì)持有鎖線程的引用置為null
  4. 喚醒等待隊(duì)列中的某個(gè)線程
//ReentrantLock
public void unlock() {sync.release(1);
}
//Sync extends AQS
public final boolean release(int arg) {//解鎖
    if (tryRelease(arg)) {Node h = head;
        //如果解鎖完成,如果隊(duì)列中有元素,則喚醒隊(duì)列中的某個(gè)線程
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

//Sync
protected final boolean tryRelease(int releases) {//計(jì)算本次解鎖后 state 的值
        int c = getState() - releases;
    	//如果要解鎖的不是持有鎖的線程,說(shuō)明程序出了問(wèn)題
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
    	//如果解鎖后的值為 0,說(shuō)明徹底解鎖
        if (c == 0) {free = true;
            //去掉 AQS 對(duì)持有鎖線程的引用
            setExclusiveOwnerThread(null);
        }
    	//設(shè)置新的state值
        setState(c);
        return free;
}

其中,解鎖后需要喚醒隊(duì)列中的某個(gè)線程,主要流程是:

  1. 如果隊(duì)列中有元素,就會(huì)進(jìn)入 unparkSuccessor() 進(jìn)行喚醒
  2. 將node的waitStatus值設(shè)為0,變?yōu)槌跏蓟癄顟B(tài)
  3. 獲取其后繼節(jié)點(diǎn)
    1. 如果有后繼節(jié)點(diǎn),則喚醒該節(jié)點(diǎn)
    2. 如果沒(méi)有后繼節(jié)點(diǎn),或者后繼節(jié)點(diǎn)是廢棄的(waitStatus=1),從隊(duì)尾往前循環(huán)找到下一個(gè)可用的前驅(qū)節(jié)點(diǎn),并喚醒它
      1. 如果全是廢棄的,那么什么也不做。
private void unparkSuccessor(Node node) {//獲取頭結(jié)點(diǎn)的waitStatus
        int ws = node.waitStatus;
        if (ws< 0)
            //如果頭結(jié)點(diǎn)是個(gè)被復(fù)用的空節(jié)點(diǎn),把它設(shè)置為初始化狀態(tài),即waitStatus = 0
            compareAndSetWaitStatus(node, ws, 0);
		//獲取頭結(jié)點(diǎn)的后繼節(jié)點(diǎn)
        Node s = node.next;
    	//如果沒(méi)有后繼節(jié)點(diǎn),或者后繼節(jié)點(diǎn)廢棄
        if (s == null || s.waitStatus >0) {s = null;
            //從隊(duì)尾往前尋找一個(gè)可用的節(jié)點(diǎn)
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus<= 0)
                    s = t;
        }
    	//如果最后找到一個(gè)可用的節(jié)點(diǎn),那么喚醒其綁定的線程
        if (s != null)
            LockSupport.unpark(s.thread);
}

至此,ReentrantLock 的加鎖與解鎖全部分析完成。最后附上非公平鎖的加鎖時(shí)序圖:

3.為什么Sync實(shí)現(xiàn)nonfairTryAcquire()?

因?yàn)?tryLock() 沒(méi)有公平與非公平的概念,都是走非公平的邏輯,調(diào)用sync.nonfaireTryAquire(),即:

  1. 如果鎖空閑,則CAS一次,嘗試獲取鎖
  2. 如果鎖非空閑,但可重入,則重入
  3. 1和2都失敗,則return false。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

當(dāng)前標(biāo)題:Java多線程(二)——ReentrantLock源碼分析-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://www.chinadenli.net/article36/dcsesg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站建設(shè)自適應(yīng)網(wǎng)站關(guān)鍵詞優(yōu)化App設(shè)計(jì)用戶體驗(yàn)ChatGPT

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司