久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

java ReentrantLock 分析

 流曲頻陽 2017-05-31


并發(fā)編程中經(jīng)常用到的莫非是這個ReentrantLock這個類,線程獲取鎖和釋放鎖,。還有一個則是synchronized,,常用來多線程控制獲取鎖機制。


先寫一個簡單的例子,。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.multi.thread;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class AQSDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock(true);
        MyThread t1 = new MyThread("t1", lock);
        MyThread t2 = new MyThread("t2", lock);
        MyThread t3 = new MyThread("t3", lock);
        t1.start();
        t2.start();
        t3.start();
    }
     
}
class MyThread extends Thread {
    private Lock lock;
    public MyThread(String name, Lock lock) {
        super(name);
        this.lock = lock;
    }
    @Override
    public void run() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread() + "  is running ");
            try {
                Thread.sleep(500);
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        finally {
            lock.unlock();
        }
    }
}


這是個簡單的用ReentrantLock的代碼,。


知識點理解:

ReentranctLock:

1) 可重入性:大致意思就是如果一個函數(shù)能被安全的重復(fù)執(zhí)行,那么這個函數(shù)是可重復(fù)的。聽起來很繞口,。

2)可重入鎖:一個線程可以重復(fù)的獲取它已經(jīng)擁有的鎖,。


特性:

1)ReentrantLock可以在不同的方法中使用。

2)支持公平鎖和非公平鎖概念

    static final class NonfairSync extends Sync,;(非公平鎖)

    static final class FairSync extends Sync,;(公平鎖)

3)支持中斷鎖,收到中斷信號可以釋放其擁有的鎖,。

4)支持超時獲取鎖:tryLock方法是嘗試獲取鎖,,支持獲取鎖的是帶上時間限制,等待一定時間就會返回,。


ReentrantLock就先簡單說一下AQS(AbstractQueuedSynchronizer),。java.util.concurrent包下很多類都是基于AQS作為基礎(chǔ)開發(fā)的,Condition,BlockingQueue以及線程池使用的worker都是基于起實現(xiàn)的,,其實就是將負(fù)雜的繁瑣的并發(fā)過程封裝起來,,以便其他的開發(fā)工具更容易的開發(fā)。其主要通過volatile和Unsafe類的原子操作,,來實現(xiàn)阻塞和同步,。


AQS是一個抽象類,其他類主要通過重載其tryAcquire(int arg)來獲取鎖,,和tryRelease來釋放鎖,。

AQS不在這里做分析,會有單獨的一篇文章來學(xué)習(xí)AQS,。


ReentrantLock類里面主要有三個類,,Sync,NonfairSync,,F(xiàn)airSync這三個類,,NonfairSync與FairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類,。


Sync是ReentrantLock實現(xiàn)公平鎖和非公平鎖的主要實現(xiàn),,默認(rèn)情況下ReentrantLock是非公平鎖。

    Lock lock = new ReentrantLock(true);   :true則是公平鎖,,false就是非公平鎖,,什么都不傳也是非公平鎖默認(rèn)的。


非公平鎖:

    lock.lock(),;點進去代碼會進入到,,ReentranctLock內(nèi)部類Sync。

    

1
2
3
4
5
6
7
8
9
10
11
    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();
         
        ......省略,。
      }

這個抽象類Sync的里有一個抽象方法,,lock(),,供給NonfairSync,F(xiàn)airSync這兩個實現(xiàn)類來實現(xiàn)的,。這個是一個模板方法設(shè)計模式,,具體的邏輯供給子類來實現(xiàn)。

非公平鎖的lock的方法,,雖然都可以自己看,,但是還是粘貼出來,說一下,。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 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(01))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        ......省略
    }

其實重點看這個compareAndSetState(0,1),,這個其實一個原子操作,是cas操作來獲取線程的資源的,。其代表的是如果原來的值是0就將其設(shè)為1,,并且返回true。其實這段代碼就是設(shè)置private volatile int state;,,這個狀態(tài)的,。

其實現(xiàn)原理就是通過Unsafe直接得到state的內(nèi)存地址然后直接操作內(nèi)存的。設(shè)置成功,,就說明已經(jīng)獲取到了鎖,,如果失敗的,則會進入:

1
2
3
4
5
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

這個方法里,,這個過程是先去判斷鎖的狀態(tài)是否為可用,,如果鎖已被持有,則再判斷持有鎖的線程是否未當(dāng)前線程,,如果是則將鎖的持有遞增,這也是java層實現(xiàn)可重入性的原理,。如果再次失敗,,則進入等待隊列。就是要進去等待隊列了AQS有一個內(nèi)部類,,是Node就是用來存放獲取鎖的線程信息,。


AQS的線程阻塞隊列是一個雙向隊列,提供了FiFO的特性,,Head節(jié)點表示頭部,,tail表示尾部。

1)節(jié)點node,維護一個volatile狀態(tài),,維護一個prev指向向前一個隊列節(jié)點,,根據(jù)前一個節(jié)點的狀態(tài)來判斷是否獲取鎖。

2)當(dāng)前線程釋放的時候,,只需要修改自身的狀態(tài)即可,,后續(xù)節(jié)點會觀察到這個volatile狀態(tài)而改變獲取鎖,。volatile是放在內(nèi)存中的,共享的,,所以前一個節(jié)點改變狀態(tài)后,,后續(xù)節(jié)點會看到這個狀態(tài)信息。


獲取鎖失敗后就會加入到隊列里,,但是有一點,,不公平鎖就是,每個新來的線程來獲取所得時候,,不是直接放入到隊列尾部,,而是也去cas修改state狀態(tài),看看是否獲取鎖成功,。


總結(jié)非公平鎖:

首先會嘗試改變AQS的狀態(tài),,改變成功了就獲取鎖,否則失敗后再次通過判斷當(dāng)前的state的狀態(tài)是否為0,,如果為0,,就再次嘗試獲取鎖。如果state不為0,,該鎖已經(jīng)被其他線程持有了,,但是其它線程也可能也是自己啊,所以也要判斷一下是否是自己獲取線程,,如果是則是獲取成功,,且鎖的次數(shù)要加1,這是可重入鎖,,不是則加入到node阻塞隊列里,。加入到隊列后則在for循環(huán)中通過判斷當(dāng)前線程狀態(tài)來決定是否喲啊阻塞。可以看出在加入隊列前及阻塞前多次嘗試去獲取鎖,,而避免進入線程阻塞,,這是因為阻塞、喚醒都需要cpu的調(diào)度,,以及上下文切換,,這是個重量級的操作,應(yīng)盡量避免,。


公平鎖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
FairSync類:
final void lock() {
   //先去判斷鎖的狀態(tài),,而不是直接去獲取
    acquire(1);
}
AQS類:
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
FairSync類:
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
     
    //hasQueuedPredecessors判斷是否有前節(jié)點,如果有就不會嘗試去獲取鎖
        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;
}


公平鎖,,主要區(qū)別是:什么事都要有個先來后到,,先來的有先。獲取鎖的時候是先看鎖是否可用并且是否有節(jié)點,,就是是否有阻塞隊列,。有的話,,就是直接放入到隊列尾部,而不是獲取鎖,。


釋放鎖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        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;
}


釋放鎖是很簡單的,,就是先去改變state這個狀態(tài)的值,改變后如果狀態(tài)為0,,則說明釋放成功了,,如果直接可重入了多次,也要釋放很多次的鎖,。


釋放過程:

Head節(jié)點就是當(dāng)前持有鎖的線程節(jié)點,,當(dāng)釋放鎖時,從頭結(jié)點的next來看,,頭結(jié)點的下一個節(jié)點如果不為null,且waitStatus不大于0,,則跳過判斷,否則從隊尾向前找到最前的一個waitStatus的節(jié)點,,然后通過LockSupport.unpark(s.thread)喚醒該節(jié)點線程,。可以看出ReentrantLock的非公平鎖只是在獲取鎖的時候是非公平的,,如果進入到等待隊列后,,在head節(jié)點的線程unlock()時,會按照進入的順序來得到喚醒,,保證了隊列的FIFO的特性,。


參考文章:

http:///2017/01/09/%E7%94%B1ReentrantLock%E5%88%86%E6%9E%90JUC%E7%9A%84%E6%A0%B8%E5%BF%83AQS/


http://www.cnblogs.com/leesf456/p/5383609.html


https://github.com/pzxwhc/MineKnowContainer/issues/16


http://www./24006.html



本文出自 “10093778” 博客,請務(wù)必保留此出處http://10103778.blog.51cto.com/10093778/1930644

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多