国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - ReentrantLock實(shí)現(xiàn)原理詳解

ReentrantLock實(shí)現(xiàn)原理詳解

2020-08-12 10:56mayday芋頭 Java教程

本文將對(duì)ReentrantLock實(shí)現(xiàn)原理進(jìn)行詳細(xì)的介紹,具有很好的參考價(jià)值,下面跟著小編一起來看下吧

以下是本篇文章的大綱

1 synchronized和lock

    1.1 synchronized的局限性
    1.2 lock簡(jiǎn)介

2 aqs

3 lock()與unlock()實(shí)現(xiàn)原理

    3.1 基礎(chǔ)知識(shí)
    3.2 內(nèi)部結(jié)構(gòu)
    3.3 nonfairsync
    3.3.1 lock()
    3.3.2 unlock()
    3.3.3 小結(jié)
    3.4 fairsync

4 超時(shí)機(jī)制

5 總結(jié)

1 synchronized和lock

1.1 synchronized的局限性

synchronized是java內(nèi)置的關(guān)鍵字,它提供了一種獨(dú)占的加鎖方式。synchronized的獲取和釋放鎖由jvm實(shí)現(xiàn),用戶不需要顯示的釋放鎖,非常方便。然而synchronized也有一定的局限性,例如:

當(dāng)線程嘗試獲取鎖的時(shí)候,如果獲取不到鎖會(huì)一直阻塞。

如果獲取鎖的線程進(jìn)入休眠或者阻塞,除非當(dāng)前線程異常,否則其他線程嘗試獲取鎖必須一直等待。

jdk1.5之后發(fā)布,加入了doug lea實(shí)現(xiàn)的concurrent包。包內(nèi)提供了lock類,用來提供更多擴(kuò)展的加鎖功能。lock彌補(bǔ)了synchronized的局限,提供了更加細(xì)粒度的加鎖功能。

1.2 lock簡(jiǎn)介

lock api如下

?
1
2
3
4
5
6
void lock();
void lockinterruptibly() throws interruptedexception;
boolean trylock();
boolean trylock(long time, timeunit unit) throws interruptedexception;
void unlock();
condition newcondition();

其中最常用的就是lock和unlock操作了。因?yàn)槭褂胠ock時(shí),需要手動(dòng)的釋放鎖,所以需要使用try..catch來包住業(yè)務(wù)代碼,并且在finally中釋放鎖。典型使用如下

?
1
2
3
4
5
6
7
8
9
10
11
private lock lock = new reentrantlock();
public void test(){
 lock.lock();
 try{
 dosomething();
 }catch (exception e){
 // ignored
 }finally {
 lock.unlock();
 }
}

2 aqs

abstractqueuedsynchronizer簡(jiǎn)稱aqs,是一個(gè)用于構(gòu)建鎖和同步容器的框架。事實(shí)上concurrent包內(nèi)許多類都是基于aqs構(gòu)建,例如reentrantlock,semaphore,countdownlatch,reentrantreadwritelock,futuretask等。aqs解決了在實(shí)現(xiàn)同步容器時(shí)設(shè)計(jì)的大量細(xì)節(jié)問題。

aqs使用一個(gè)fifo的隊(duì)列表示排隊(duì)等待鎖的線程,隊(duì)列頭節(jié)點(diǎn)稱作“哨兵節(jié)點(diǎn)”或者“啞節(jié)點(diǎn)”,它不與任何線程關(guān)聯(lián)。其他的節(jié)點(diǎn)與等待線程關(guān)聯(lián),每個(gè)節(jié)點(diǎn)維護(hù)一個(gè)等待狀態(tài)waitstatus。如圖

ReentrantLock實(shí)現(xiàn)原理詳解

aqs中還有一個(gè)表示狀態(tài)的字段state,例如reentrantlocky用它表示線程重入鎖的次數(shù),semaphore用它表示剩余的許可數(shù)量,futuretask用它表示任務(wù)的狀態(tài)。對(duì)state變量值的更新都采用cas操作保證更新操作的原子性。

abstractqueuedsynchronizer繼承了abstractownablesynchronizer,這個(gè)類只有一個(gè)變量:exclusiveownerthread,表示當(dāng)前占用該鎖的線程,并且提供了相應(yīng)的get,set方法。

理解aqs可以幫助我們更好的理解jcu包中的同步容器。

3 lock()與unlock()實(shí)現(xiàn)原理

3.1 基礎(chǔ)知識(shí)

reentrantlock是lock的默認(rèn)實(shí)現(xiàn)之一。那么lock()和unlock()是怎么實(shí)現(xiàn)的呢?首先我們要弄清楚幾個(gè)概念

可重入鎖。可重入鎖是指同一個(gè)線程可以多次獲取同一把鎖。reentrantlock和synchronized都是可重入鎖。

可中斷鎖。可中斷鎖是指線程嘗試獲取鎖的過程中,是否可以響應(yīng)中斷。synchronized是不可中斷鎖,而reentrantlock則提供了中斷功能。

公平鎖與非公平鎖。公平鎖是指多個(gè)線程同時(shí)嘗試獲取同一把鎖時(shí),獲取鎖的順序按照線程達(dá)到的順序,而非公平鎖則允許線程“插隊(duì)”。synchronized是非公平鎖,而reentrantlock的默認(rèn)實(shí)現(xiàn)是非公平鎖,但是也可以設(shè)置為公平鎖。

cas操作(compareandswap)。cas操作簡(jiǎn)單的說就是比較并交換。cas 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(v)、預(yù)期原值(a)和新值(b)。如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會(huì)在 cas 指令之前返回該位置的值。cas 有效地說明了“我認(rèn)為位置 v 應(yīng)該包含值 a;如果包含該值,則將 b 放到這個(gè)位置;否則,不要更改該位置,只告訴我這個(gè)位置現(xiàn)在的值即可。” java并發(fā)包(java.util.concurrent)中大量使用了cas操作,涉及到并發(fā)的地方都調(diào)用了sun.misc.unsafe類方法進(jìn)行cas操作。

3.2 內(nèi)部結(jié)構(gòu)

reentrantlock提供了兩個(gè)構(gòu)造器,分別是

?
1
2
3
4
5
6
public reentrantlock() {
 sync = new nonfairsync();
}
public reentrantlock(boolean fair) {
 sync = fair ? new fairsync() : new nonfairsync();
}

默認(rèn)構(gòu)造器初始化為nonfairsync對(duì)象,即非公平鎖,而帶參數(shù)的構(gòu)造器可以指定使用公平鎖和非公平鎖。由lock()和unlock的源碼可以看到,它們只是分別調(diào)用了sync對(duì)象的lock()和release(1)方法。

sync是reentrantlock的內(nèi)部類,它的結(jié)構(gòu)如下

ReentrantLock實(shí)現(xiàn)原理詳解

可以看到sync擴(kuò)展了abstractqueuedsynchronizer。

3.3 nonfairsync

我們從源代碼出發(fā),分析非公平鎖獲取鎖和釋放鎖的過程。

3.3.1 lock()

lock()源碼如下

?
1
2
3
4
5
6
final void lock() {
 if (compareandsetstate(0, 1))
 setexclusiveownerthread(thread.currentthread());
 else
 acquire(1);
}

首先用一個(gè)cas操作,判斷state是否是0(表示當(dāng)前鎖未被占用),如果是0則把它置為1,并且設(shè)置當(dāng)前線程為該鎖的獨(dú)占線程,表示獲取鎖成功。當(dāng)多個(gè)線程同時(shí)嘗試占用同一個(gè)鎖時(shí),cas操作只能保證一個(gè)線程操作成功,剩下的只能乖乖的去排隊(duì)啦。

“非公平”即體現(xiàn)在這里,如果占用鎖的線程剛釋放鎖,state置為0,而排隊(duì)等待鎖的線程還未喚醒時(shí),新來的線程就直接搶占了該鎖,那么就“插隊(duì)”了。

若當(dāng)前有三個(gè)線程去競(jìng)爭(zhēng)鎖,假設(shè)線程a的cas操作成功了,拿到了鎖開開心心的返回了,那么線程b和c則設(shè)置state失敗,走到了else里面。我們往下看acquire。

acquire(arg)

?
1
2
3
4
5
public final void acquire(int arg) {
 if (!tryacquire(arg) &&
 acquirequeued(addwaiter(node.exclusive), arg))
 selfinterrupt();
}

代碼非常簡(jiǎn)潔,但是背后的邏輯卻非常復(fù)雜,可見doug lea大神的編程功力。

1. 第一步。嘗試去獲取鎖。如果嘗試獲取鎖成功,方法直接返回。

tryacquire(arg)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
final boolean nonfairtryacquire(int acquires) {
 //獲取當(dāng)前線程
 final thread current = thread.currentthread();
 //獲取state變量值
 int c = getstate();
 if (c == 0) { //沒有線程占用鎖
 if (compareandsetstate(0, acquires)) {
 //占用鎖成功,設(shè)置獨(dú)占線程為當(dāng)前線程
 setexclusiveownerthread(current);
 return true;
 }
 } else if (current == getexclusiveownerthread()) { //當(dāng)前線程已經(jīng)占用該鎖
 int nextc = c + acquires;
 if (nextc < 0) // overflow
 throw new error("maximum lock count exceeded");
 // 更新state值為新的重入次數(shù)
 setstate(nextc);
 return true;
 }
 //獲取鎖失敗
 return false;
}

非公平鎖tryacquire的流程是:檢查state字段,若為0,表示鎖未被占用,那么嘗試占用,若不為0,檢查當(dāng)前鎖是否被自己占用,若被自己占用,則更新state字段,表示重入鎖的次數(shù)。如果以上兩點(diǎn)都沒有成功,則獲取鎖失敗,返回false。

2. 第二步,入隊(duì)。由于上文中提到線程a已經(jīng)占用了鎖,所以b和c執(zhí)行tryacquire失敗,并且入等待隊(duì)列。如果線程a拿著鎖死死不放,那么b和c就會(huì)被掛起。

先看下入隊(duì)的過程。

先看addwaiter(node.exclusive)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * 將新節(jié)點(diǎn)和當(dāng)前線程關(guān)聯(lián)并且入隊(duì)列
 * @param mode 獨(dú)占/共享
 * @return 新節(jié)點(diǎn)
 */
private node addwaiter(node mode) {
 //初始化節(jié)點(diǎn),設(shè)置關(guān)聯(lián)線程和模式(獨(dú)占 or 共享)
 node node = new node(thread.currentthread(), mode);
 // 獲取尾節(jié)點(diǎn)引用
 node pred = tail;
 // 尾節(jié)點(diǎn)不為空,說明隊(duì)列已經(jīng)初始化過
 if (pred != null) {
 node.prev = pred;
 // 設(shè)置新節(jié)點(diǎn)為尾節(jié)點(diǎn)
 if (compareandsettail(pred, node)) {
 pred.next = node;
 return node;
 }
 }
 // 尾節(jié)點(diǎn)為空,說明隊(duì)列還未初始化,需要初始化head節(jié)點(diǎn)并入隊(duì)新節(jié)點(diǎn)
 enq(node);
 return node;
}

b、c線程同時(shí)嘗試入隊(duì)列,由于隊(duì)列尚未初始化,tail==null,故至少會(huì)有一個(gè)線程會(huì)走到enq(node)。我們假設(shè)同時(shí)走到了enq(node)里。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * 初始化隊(duì)列并且入隊(duì)新節(jié)點(diǎn)
 */
private node enq(final node node) {
 //開始自旋
 for (;;) {
 node t = tail;
 if (t == null) { // must initialize
 // 如果tail為空,則新建一個(gè)head節(jié)點(diǎn),并且tail指向head
 if (compareandsethead(new node()))
 tail = head;
 } else {
 node.prev = t;
 // tail不為空,將新節(jié)點(diǎn)入隊(duì)
 if (compareandsettail(t, node)) {
 t.next = node;
 return t;
 }
 }
 }
}

這里體現(xiàn)了經(jīng)典的自旋+cas組合來實(shí)現(xiàn)非阻塞的原子操作。由于compareandsethead的實(shí)現(xiàn)使用了unsafe類提供的cas操作,所以只有一個(gè)線程會(huì)創(chuàng)建head節(jié)點(diǎn)成功。假設(shè)線程b成功,之后b、c開始第二輪循環(huán),此時(shí)tail已經(jīng)不為空,兩個(gè)線程都走到else里面。假設(shè)b線程compareandsettail成功,那么b就可以返回了,c由于入隊(duì)失敗還需要第三輪循環(huán)。最終所有線程都可以成功入隊(duì)。

當(dāng)b、c入等待隊(duì)列后,此時(shí)aqs隊(duì)列如下:

ReentrantLock實(shí)現(xiàn)原理詳解

3. 第三步,掛起。b和c相繼執(zhí)行acquirequeued(final node node, int arg)。這個(gè)方法讓已經(jīng)入隊(duì)的線程嘗試獲取鎖,若失敗則會(huì)被掛起。

?
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
/**
 * 已經(jīng)入隊(duì)的線程嘗試獲取鎖
 */
final boolean acquirequeued(final node node, int arg) {
 boolean failed = true; //標(biāo)記是否成功獲取鎖
 try {
 boolean interrupted = false; //標(biāo)記線程是否被中斷過
 for (;;) {
 final node p = node.predecessor(); //獲取前驅(qū)節(jié)點(diǎn)
 //如果前驅(qū)是head,即該結(jié)點(diǎn)已成老二,那么便有資格去嘗試獲取鎖
 if (p == head && tryacquire(arg)) {
 sethead(node); // 獲取成功,將當(dāng)前節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn)
 p.next = null; // 原h(huán)ead節(jié)點(diǎn)出隊(duì),在某個(gè)時(shí)間點(diǎn)被gc回收
 failed = false; //獲取成功
 return interrupted; //返回是否被中斷過
 }
 // 判斷獲取失敗后是否可以掛起,若可以則掛起
 if (shouldparkafterfailedacquire(p, node) &&
  parkandcheckinterrupt())
 // 線程若被中斷,設(shè)置interrupted為true
 interrupted = true;
 }
 } finally {
 if (failed)
 cancelacquire(node);
 }
}

code里的注釋已經(jīng)很清晰的說明了acquirequeued的執(zhí)行流程。假設(shè)b和c在競(jìng)爭(zhēng)鎖的過程中a一直持有鎖,那么它們的tryacquire操作都會(huì)失敗,因此會(huì)走到第2個(gè)if語句中。我們?cè)倏聪聅houldparkafterfailedacquire和parkandcheckinterrupt都做了哪些事吧。

?
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
/**
 * 判斷當(dāng)前線程獲取鎖失敗之后是否需要掛起.
 */
private static boolean shouldparkafterfailedacquire(node pred, node node) {
 //前驅(qū)節(jié)點(diǎn)的狀態(tài)
 int ws = pred.waitstatus;
 if (ws == node.signal)
 // 前驅(qū)節(jié)點(diǎn)狀態(tài)為signal,返回true
 return true;
 // 前驅(qū)節(jié)點(diǎn)狀態(tài)為cancelled
 if (ws > 0) {
 // 從隊(duì)尾向前尋找第一個(gè)狀態(tài)不為cancelled的節(jié)點(diǎn)
 do {
 node.prev = pred = pred.prev;
 } while (pred.waitstatus > 0);
 pred.next = node;
 } else {
 // 將前驅(qū)節(jié)點(diǎn)的狀態(tài)設(shè)置為signal
 compareandsetwaitstatus(pred, ws, node.signal);
 }
 return false;
}
/**
 * 掛起當(dāng)前線程,返回線程中斷狀態(tài)并重置
 */
private final boolean parkandcheckinterrupt() {
 locksupport.park(this);
 return thread.interrupted();
}

線程入隊(duì)后能夠掛起的前提是,它的前驅(qū)節(jié)點(diǎn)的狀態(tài)為signal,它的含義是“hi,前面的兄弟,如果你獲取鎖并且出隊(duì)后,記得把我喚醒!”。所以shouldparkafterfailedacquire會(huì)先判斷當(dāng)前節(jié)點(diǎn)的前驅(qū)是否狀態(tài)符合要求,若符合則返回true,然后調(diào)用parkandcheckinterrupt,將自己掛起。如果不符合,再看前驅(qū)節(jié)點(diǎn)是否>0(cancelled),若是那么向前遍歷直到找到第一個(gè)符合要求的前驅(qū),若不是則將前驅(qū)節(jié)點(diǎn)的狀態(tài)設(shè)置為signal。

整個(gè)流程中,如果前驅(qū)結(jié)點(diǎn)的狀態(tài)不是signal,那么自己就不能安心掛起,需要去找個(gè)安心的掛起點(diǎn),同時(shí)可以再嘗試下看有沒有機(jī)會(huì)去嘗試競(jìng)爭(zhēng)鎖。

最終隊(duì)列可能會(huì)如下圖所示

ReentrantLock實(shí)現(xiàn)原理詳解

線程b和c都已經(jīng)入隊(duì),并且都被掛起。當(dāng)線程a釋放鎖的時(shí)候,就會(huì)去喚醒線程b去獲取鎖啦。

3.3.2 unlock()

unlock相對(duì)于lock就簡(jiǎn)單很多。源碼如下

?
1
2
3
4
5
6
7
8
9
10
11
12
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;
}

如果理解了加鎖的過程,那么解鎖看起來就容易多了。流程大致為先嘗試釋放鎖,若釋放成功,那么查看頭結(jié)點(diǎn)的狀態(tài)是否為signal,如果是則喚醒頭結(jié)點(diǎn)的下個(gè)節(jié)點(diǎn)關(guān)聯(lián)的線程,如果釋放失敗那么返回false表示解鎖失敗。這里我們也發(fā)現(xiàn)了,每次都只喚起頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)關(guān)聯(lián)的線程。

最后我們?cè)倏聪聇ryrelease的執(zhí)行過程

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * 釋放當(dāng)前線程占用的鎖
 * @param releases
 * @return 是否釋放成功
 */
protected final boolean tryrelease(int releases) {
 // 計(jì)算釋放后state值
 int c = getstate() - releases;
 // 如果不是當(dāng)前線程占用鎖,那么拋出異常
 if (thread.currentthread() != getexclusiveownerthread())
 throw new illegalmonitorstateexception();
 boolean free = false;
 if (c == 0) {
 // 鎖被重入次數(shù)為0,表示釋放成功
 free = true;
 // 清空獨(dú)占線程
 setexclusiveownerthread(null);
 }
 // 更新state值
 setstate(c);
 return free;
}

這里入?yún)?。tryrelease的過程為:當(dāng)前釋放鎖的線程若不持有鎖,則拋出異常。若持有鎖,計(jì)算釋放后的state值是否為0,若為0表示鎖已經(jīng)被成功釋放,并且則清空獨(dú)占線程,最后更新state值,返回free。

3.3.3 小結(jié)

用一張流程圖總結(jié)一下非公平鎖的獲取鎖的過程。   

ReentrantLock實(shí)現(xiàn)原理詳解

3.4 fairsync

公平鎖和非公平鎖不同之處在于,公平鎖在獲取鎖的時(shí)候,不會(huì)先去檢查state狀態(tài),而是直接執(zhí)行aqcuire(1),這里不再贅述。   

4 超時(shí)機(jī)制

在reetrantlock的trylock(long timeout, timeunit unit) 提供了超時(shí)獲取鎖的功能。它的語義是在指定的時(shí)間內(nèi)如果獲取到鎖就返回true,獲取不到則返回false。這種機(jī)制避免了線程無限期的等待鎖釋放。那么超時(shí)的功能是怎么實(shí)現(xiàn)的呢?我們還是用非公平鎖為例來一探究竟。

?
1
2
3
4
public boolean trylock(long timeout, timeunit unit)
 throws interruptedexception {
 return sync.tryacquirenanos(1, unit.tonanos(timeout));
}

還是調(diào)用了內(nèi)部類里面的方法。我們繼續(xù)向前探究

?
1
2
3
4
5
6
7
public final boolean tryacquirenanos(int arg, long nanostimeout)
 throws interruptedexception {
 if (thread.interrupted())
 throw new interruptedexception();
 return tryacquire(arg) ||
 doacquirenanos(arg, nanostimeout);
}

這里的語義是:如果線程被中斷了,那么直接拋出interruptedexception。如果未中斷,先嘗試獲取鎖,獲取成功就直接返回,獲取失敗則進(jìn)入doacquirenanos。tryacquire我們已經(jīng)看過,這里重點(diǎn)看一下doacquirenanos做了什么。

?
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
/**
 * 在有限的時(shí)間內(nèi)去競(jìng)爭(zhēng)鎖
 * @return 是否獲取成功
 */
private boolean doacquirenanos(int arg, long nanostimeout)
 throws interruptedexception {
 // 起始時(shí)間
 long lasttime = system.nanotime();
 // 線程入隊(duì)
 final node node = addwaiter(node.exclusive);
 boolean failed = true;
 try {
 // 又是自旋!
 for (;;) {
 // 獲取前驅(qū)節(jié)點(diǎn)
 final node p = node.predecessor();
 // 如果前驅(qū)是頭節(jié)點(diǎn)并且占用鎖成功,則將當(dāng)前節(jié)點(diǎn)變成頭結(jié)點(diǎn)
 if (p == head && tryacquire(arg)) {
 sethead(node);
 p.next = null; // help gc
 failed = false;
 return true;
 }
 // 如果已經(jīng)超時(shí),返回false
 if (nanostimeout <= 0)
 return false;
 // 超時(shí)時(shí)間未到,且需要掛起
 if (shouldparkafterfailedacquire(p, node) &&
  nanostimeout > spinfortimeoutthreshold)
 // 阻塞當(dāng)前線程直到超時(shí)時(shí)間到期
 locksupport.parknanos(this, nanostimeout);
 long now = system.nanotime();
 // 更新nanostimeout
 nanostimeout -= now - lasttime;
 lasttime = now;
 if (thread.interrupted())
 //相應(yīng)中斷
 throw new interruptedexception();
 }
 } finally {
 if (failed)
 cancelacquire(node);
 }
}

doacquirenanos的流程簡(jiǎn)述為:線程先入等待隊(duì)列,然后開始自旋,嘗試獲取鎖,獲取成功就返回,失敗則在隊(duì)列里找一個(gè)安全點(diǎn)把自己掛起直到超時(shí)時(shí)間過期。這里為什么還需要循環(huán)呢?因?yàn)楫?dāng)前線程節(jié)點(diǎn)的前驅(qū)狀態(tài)可能不是signal,那么在當(dāng)前這一輪循環(huán)中線程不會(huì)被掛起,然后更新超時(shí)時(shí)間,開始新一輪的嘗試

5 總結(jié)

reentrantlock的核心功能講解差不多落下帷幕,理解aqs,就很容易理解reentrantlock的實(shí)現(xiàn)原理。文中慘雜著筆者的個(gè)人理解,如有不正之處,還望指正。

 以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持服務(wù)器之家!

原文鏈接:http://www.cnblogs.com/maypattis/p/6403682.html

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 亚洲精品视频在线看 | 草久在线观看 | 国内美女人妻一级毛片免费看 | 欧美美女爱爱 | 精品久久av | 99在线精品视频 | 欧美91在线 | 国产精品日本一区二区不卡视频 | 性色好看的网站 | 999精品视频 | 久久精品视频免费观看 | 国产精品入口在线观看 | 99精品一区二区三区 | 国产伦精品一区二区三区精品视频 | 五月天激情综合网 | 免费一级特黄做受大片 | 天堂中文在线8 | 免费一级在线观看 | 精品成人免费一区二区在线播放 | 亚洲精品短视频 | 亚洲欧美在线观看 | 欧美福利在线观看 | 欧洲精品 | 成人片在线播放 | 久久精品国产亚洲 | 自拍偷拍 亚洲 | 精品国产乱码久久久久久1区2区 | 日本一区二区三区中文字幕 | 正在播放国产精品 | 国产精品久久国产精品 | 国产一区二区三区四区在线观看 | 久久久久久综合 | 国产成人综合av | 91丁香婷婷综合久久欧美 | 欧美性猛交一区二区三区精品 | 欧美激情在线观看 | 日韩中文字幕视频在线观看 | 国产中文字幕在线看 | 久久久久久久久久久九 | 国产精品免费在线 | 96自拍视频 |