精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

上難度了!社招三年了,我要跳槽了!

開發(fā) 前端
在 JDK 1.7 中它使用的是數(shù)組加鏈表的形式實現(xiàn)的,而數(shù)組又分為:大數(shù)組 Segment 和小數(shù)組 HashEntry。Segment 是一種可重入鎖(ReentrantLock),在 ConcurrentHashMap 里扮演鎖的角色;HashEntry 則用于存儲鍵值對數(shù)據(jù)。

Java

HashMap底層實現(xiàn)原理

  • 在 JDK 1.7 版本之前, HashMap 數(shù)據(jù)結(jié)構(gòu)是數(shù)組和鏈表,HashMap通過哈希算法將元素的鍵(Key)映射到數(shù)組中的槽位(Bucket)。如果多個鍵映射到同一個槽位,它們會以鏈表的形式存儲在同一個槽位上,因為鏈表的查詢時間是O(n),所以沖突很嚴(yán)重,一個索引上的鏈表非常長,效率就很低了。

圖片圖片

  • 所以在 JDK 1.8 版本的時候做了優(yōu)化,當(dāng)一個鏈表的長度超過8的時候就轉(zhuǎn)換數(shù)據(jù)結(jié)構(gòu),不再使用鏈表存儲,而是使用紅黑樹,查找時使用紅黑樹,時間復(fù)雜度O(log n),可以提高查詢性能,但是在數(shù)量較少時,即數(shù)量小于6時,會將紅黑樹轉(zhuǎn)換回鏈表。

圖片圖片

介紹一下ConcurrentHashMap;

  • 在 JDK 1.7 中它使用的是數(shù)組加鏈表的形式實現(xiàn)的,而數(shù)組又分為:大數(shù)組 Segment 和小數(shù)組 HashEntry。Segment 是一種可重入鎖(ReentrantLock),在 ConcurrentHashMap 里扮演鎖的角色;HashEntry 則用于存儲鍵值對數(shù)據(jù)。一個 ConcurrentHashMap 里包含一個 Segment 數(shù)組,一個 Segment 里包含一個 HashEntry 數(shù)組,每個 HashEntry 是一個鏈表結(jié)構(gòu)的元素。簡單理解就是,ConcurrentHashMap 是一個 Segment 數(shù)組,Segment 通過繼承 ReentrantLock 來進行加鎖,所以每次需要加鎖的操作鎖住的是一個 segment,這樣只要保證每個 Segment 是線程安全的,也就實現(xiàn)了全局的線程安全。

圖片圖片

  • JDK 1.8 也引入了紅黑樹,優(yōu)化了之前的固定鏈表,那么當(dāng)數(shù)據(jù)量比較大的時候,查詢性能也得到了很大的提升,從之前的 O(n) 優(yōu)化到了 O(logn) 的時間復(fù)雜度。ConcurrentHashMap 主要通過 volatile + CAS 或者 synchronized 來實現(xiàn)的線程安全的,ConcurrentHashMap通過對頭結(jié)點加鎖來保證線程安全的,鎖的粒度相比 Segment 來說更小了,發(fā)生沖突和加鎖的頻率降低了,并發(fā)操作的性能就提高了。

圖片圖片

ThreadLocal的key是什么

為了有個宏觀的認(rèn)識,我們先來看下ThreadLocal的內(nèi)存結(jié)構(gòu)圖

圖片圖片

從內(nèi)存結(jié)構(gòu)圖,我們可以看到:

  • Thread類中,有個ThreadLocal.ThreadLocalMap 的成員變量。
  • ThreadLocalMap內(nèi)部維護了Entry數(shù)組,每個Entry代表一個完整的對象,key是ThreadLocal本身,value是ThreadLocal的泛型對象值。

關(guān)鍵源碼分析

對照著幾段關(guān)鍵源碼來看,更容易理解一點哈~我們回到Thread類源碼,可以看到成員變量ThreadLocalMap的初始值是為null

public class Thread implements Runnable {
   //ThreadLocal.ThreadLocalMap是Thread的屬性
   ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocalMap的關(guān)鍵源碼如下:

static class ThreadLocalMap {
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    //Entry數(shù)組
    private Entry[] table;
    
    // ThreadLocalMap的構(gòu)造器,ThreadLocal作為key
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        size = 1;
        setThreshold(INITIAL_CAPACITY);
    }
}

ThreadLocal類中的關(guān)鍵set()方法:

public void set(T value) {
        Thread t = Thread.currentThread(); //獲取當(dāng)前線程t
        ThreadLocalMap map = getMap(t);  //根據(jù)當(dāng)前線程獲取到ThreadLocalMap
        if (map != null)  //如果獲取的ThreadLocalMap對象不為空
            map.set(this, value); //K,V設(shè)置到ThreadLocalMap中
        else
            createMap(t, value); //創(chuàng)建一個新的ThreadLocalMap
    }
    
     ThreadLocalMap getMap(Thread t) {
       return t.threadLocals; //返回Thread對象的ThreadLocalMap屬性
    }

    void createMap(Thread t, T firstValue) { //調(diào)用ThreadLocalMap的構(gòu)造函數(shù)
        t.threadLocals = new ThreadLocalMap(this, firstValue); this表示當(dāng)前類ThreadLocal
    }

ThreadLocal類中的關(guān)鍵get()方法:

public T get() {
        Thread t = Thread.currentThread();//獲取當(dāng)前線程t
        ThreadLocalMap map = getMap(t);//根據(jù)當(dāng)前線程獲取到ThreadLocalMap
        if (map != null) { //如果獲取的ThreadLocalMap對象不為空
            //由this(即ThreadLoca對象)得到對應(yīng)的Value,即ThreadLocal的泛型值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value; 
                return result;
            }
        }
        return setInitialValue(); //初始化threadLocals成員變量的值
    }
    
     private T setInitialValue() {
        T value = initialValue(); //初始化value的值
        Thread t = Thread.currentThread(); 
        ThreadLocalMap map = getMap(t); //以當(dāng)前線程為key,獲取threadLocals成員變量,它是一個ThreadLocalMap
        if (map != null)
            map.set(this, value);  //K,V設(shè)置到ThreadLocalMap中
        else
            createMap(t, value); //實例化threadLocals成員變量
        return value;
    }

綜上所述:

  • Thread線程類有一個類型為ThreadLocal.ThreadLocalMap的實例變量threadLocals,即每個線程都有一個屬于自己的ThreadLocalMap。
  • ThreadLocalMap內(nèi)部維護著Entry數(shù)組,每個Entry代表一個完整的對象,key是ThreadLocal本身,value是ThreadLocal的泛型值。
  • 并發(fā)多線程場景下,每個線程Thread,在往ThreadLocal里設(shè)置值的時候,都是往自己的ThreadLocalMap里存,讀也是以某個ThreadLocal作為引用,在自己的map里找對應(yīng)的key,從而可以實現(xiàn)了線程隔離。

Reentranlock,公平鎖/非公平鎖的實現(xiàn)

公平鎖和非公平鎖的實現(xiàn)都是基于AbstractQueuedSynchronizer(AQS),這是Java并發(fā)框架中用于構(gòu)建鎖和同步器的基礎(chǔ)框架。ReentrantLock內(nèi)部通過繼承AbstractQueuedSynchronizer(AQS)來實現(xiàn)鎖的機制。AQS使用一個volatile int state字段來表示鎖的狀態(tài),以及一個內(nèi)部類Node來維護等待隊列。

AQS的核心方法

  • getState(): 獲取state的值。
  • setState(): 設(shè)置state的值。
  • compareAndSetState(expected, update): 原子地更新state的值。

ReentrantLock的實現(xiàn)

在ReentrantLock的構(gòu)造函數(shù)中,你可以選擇創(chuàng)建公平鎖或非公平鎖。默認(rèn)情況下,不帶參數(shù)的構(gòu)造函數(shù)創(chuàng)建非公平鎖。

public ReentrantLock() {
    sync = new NonfairSync();
}

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

NonfairSync和FairSync分別擴展了AbstractQueuedSynchronizer,并重寫了tryAcquire()方法。

非公平鎖(默認(rèn))

非公平鎖試圖在獲取鎖時立即進行CAS操作,即使已經(jīng)有其他線程在等待。

這意味著它可能會讓新來的線程“插隊”,這在理論上是不公平的,但在實踐中往往能提供更好的性能,因為減少了線程的等待時間。

在非公平鎖中,tryAcquire()方法首先嘗試獲取鎖,而不考慮是否有線程在等待隊列中。

protected final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 直接嘗試獲取鎖
        if (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;
}

公平鎖

公平鎖則會保證鎖的分配順序,即后來的線程必須等待前面的線程釋放鎖才能獲取。

這樣雖然理論上更公平,但可能增加線程等待的時間,從而影響性能。 在公平鎖中,tryAcquire()方法會檢查等待隊列中是否有線程在等待,如果有,則不會嘗試獲取鎖,除非隊列為空或當(dāng)前線程已經(jīng)是隊列的頭部。

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 檢查隊列中是否有等待線程
        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;
}

synchronize鎖升級過程

synchronized關(guān)鍵字在Java中用于控制多線程對共享資源的訪問,確保同一時刻只有一個線程能夠執(zhí)行臨界區(qū)代碼。鎖升級機制允許鎖從一種成本較低的狀態(tài)升級到成本較高的狀態(tài),具體包括以下幾種鎖狀態(tài):

  • 無鎖: 在沒有線程訪問同步塊的情況下,對象頭中的Mark Word中不包含鎖信息。
  • 偏向鎖: 就是指一個線程多次重復(fù)訪問同一段同步代碼塊時,會在對象頭和棧幀中的鎖記里面添加偏向的線程ID,以后在該線程進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需要簡單地比對一下對象頭的markword里面是否存儲著指向當(dāng)前線程的偏向鎖。減少CAS加鎖帶來的開銷。
  • 輕量級鎖:如果一個線程擁有偏向鎖,此時另一個線程嘗試使用CAS將對象頭的markword的鎖指針指向自己。如果失敗了,那么他會自旋嘗試獲取鎖。此時為輕量級鎖的狀態(tài)。
  • 重量級鎖:當(dāng)自旋次數(shù)達到閾值,或者來了第三個線程爭奪鎖時,輕量級鎖就會升級為重量級鎖。

鎖升級過程是單向的,意味著一旦鎖升級到更重的級別,它就不會降級回到更輕的級別。這種機制可以避免不必要的鎖膨脹和收縮帶來的開銷,特別是在鎖競爭較少的情況下,可以顯著提高程序的執(zhí)行效率。同時,在對象的Mark Word中記錄了鎖的當(dāng)前狀態(tài)。

Mark Word是對象頭的一部分,它包含對象的元數(shù)據(jù)信息,如哈希碼、GC分代年齡、鎖狀態(tài)標(biāo)志、線程ID等。當(dāng)鎖升級時,Mark Word中的信息也會相應(yīng)地改變,以反映新的鎖狀態(tài)。

多個任務(wù),同時達到臨界點,主線程執(zhí)行,怎么實現(xiàn)

在Java中,要實現(xiàn)多個任務(wù)同時達到某個臨界點后由主線程執(zhí)行某些操作,可以使用CountDownLatch或者CyclicBarrier這兩個工具類,它們都位于java.util.concurrent包下。

下面分別介紹這兩種方式的使用方法:

使用CountDownLatch

CountDownLatch是一個同步輔助類,它允許一個或多個線程等待直到其他線程完成了操作。它通過一個計數(shù)器來實現(xiàn)這一功能,每當(dāng)一個線程完成了操作,計數(shù)器減一;當(dāng)計數(shù)器到達零時,所有因為計數(shù)器未到達零而在await()方法上等待的線程都將被釋放。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        final int threadCount = 5;
        final CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                // 執(zhí)行一些操作...
                System.out.println(Thread.currentThread().getName() + " is ready.");
                latch.countDown();
            }).start();
        }

        // 等待所有線程完成它們的操作
        latch.await();

        System.out.println("All threads have reached the critical point. Main thread continues...");
        // 主線程繼續(xù)執(zhí)行...
    }
}

使用CyclicBarrier

CyclicBarrier與CountDownLatch類似,也允許一組線程互相等待直到到達某個公共屏障點(barrier)。但是CyclicBarrier支持在屏障點執(zhí)行一個回調(diào)操作,并且在每次所有參與線程都到達屏障點后,它可以被重用。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {

    public static void main(String[] args) {
        final int threadCount = 5;
        final CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("All threads have reached the critical point. Main thread executes callback...");
            // 主線程在這里執(zhí)行回調(diào)操作
        });

        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                // 執(zhí)行一些操作...
                System.out.println(Thread.currentThread().getName() + " is ready.");
                try {
                    barrier.await(); // 等待所有線程到達屏障點
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在這兩個例子中,多個線程各自執(zhí)行一些操作,當(dāng)它們都到達臨界點時,主線程才繼續(xù)執(zhí)行后續(xù)的操作。根據(jù)具體的應(yīng)用場景,你可以選擇使用CountDownLatch或CyclicBarrier。如果只需要一次性觸發(fā)事件,可以選擇CountDownLatch;如果需要多次循環(huán)等待所有線程到達,CyclicBarrier可能更加合適。

CyclicBarrier的實現(xiàn)

方法解析

圖片圖片

  • CyclicBarrier(int parties, Runnable barrierAction) 創(chuàng)建一個CyclicBarrier實例,parties指定參與相互等待的線程數(shù),barrierAction指定當(dāng)所有線程到達屏障點之后,首先執(zhí)行的操作,該操作由最后一個進入屏障點的線程執(zhí)行。
  • CyclicBarrier(int parties) 創(chuàng)建一個CyclicBarrier實例,parties指定參與相互等待的線程數(shù)。
  • getParties() 返回參與相互等待的線程數(shù)。
  • await() 該方法被調(diào)用時表示當(dāng)前線程已經(jīng)到達屏障點,當(dāng)前線程阻塞進入休眠狀態(tài),直到所有線程都到達屏障點,當(dāng)前線程才會被喚醒。
  • await(long timeout, TimeUnit unit) 該方法被調(diào)用時表示當(dāng)前線程已經(jīng)到達屏障點,當(dāng)前線程阻塞進入休眠狀態(tài),在timeout指定的超時時間內(nèi),等待其他參與線程到達屏障點;如果超出指定的等待時間,則拋出TimeoutException異常,如果該時間小于等于零,則此方法根本不會等待。
  • isBroken() 判斷此屏障是否處于中斷狀態(tài)。如果因為構(gòu)造或最后一次重置而導(dǎo)致中斷或超時,從而使一個或多個參與者擺脫此屏障點,或者因為異常而導(dǎo)致某個屏障操作失敗,則返回true;否則返回false。
  • reset() 將屏障重置為其初始狀態(tài)。
  • getNumberWaiting() 返回當(dāng)前在屏障處等待的參與者數(shù)目,此方法主要用于調(diào)試和斷言。

源碼解析

CyclicBarrier(int parties, Runnable barrierAction)和await()方法是CyclicBarrier的核心,本篇重點分析這兩個方法的背后實現(xiàn)原理。 首先,看一下CyclicBarrier內(nèi)聲明的一些屬性信息:

//用于保護屏障入口的鎖
private final ReentrantLock lock = new ReentrantLock();
//線程等待條件
private final Condition trip = lock.newCondition();
//記錄參與等待的線程數(shù)
private final int parties;
//當(dāng)所有線程到達屏障點之后,首先執(zhí)行的命令
private final Runnable barrierCommand;
private Generation generation = new Generation();
//實際中仍在等待的線程數(shù),每當(dāng)有一個線程到達屏障點,count值就會減一;當(dāng)一次新的運算開始后,count的值被重置為parties
private int count;

其中,Generation是CyclicBarrier的一個靜態(tài)內(nèi)部類,它只有一個boolean類型的屬性,具體代碼如下:

private static class Generation {
    boolean broken = false;
}

當(dāng)使用構(gòu)造方法創(chuàng)建CyclicBarrier實例的時候,就是給上面這些屬性賦值,

//創(chuàng)建一個CyclicBarrier實例,parties指定參與相互等待的線程數(shù),
//barrierAction指定當(dāng)所有線程到達屏障點之后,首先執(zhí)行的操作,該操作由最后一個進入屏障點的線程執(zhí)行。
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

//創(chuàng)建一個CyclicBarrier實例,parties指定參與相互等待的線程數(shù)
public CyclicBarrier(int parties) {
this(parties, null);
}

當(dāng)調(diào)用await()方法時,當(dāng)前線程已經(jīng)到達屏障點,當(dāng)前線程阻塞進入休眠狀態(tài),

//該方法被調(diào)用時表示當(dāng)前線程已經(jīng)到達屏障點,當(dāng)前線程阻塞進入休眠狀態(tài)
//直到所有線程都到達屏障點,當(dāng)前線程才會被喚醒
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen;
    }
}

//該方法被調(diào)用時表示當(dāng)前線程已經(jīng)到達屏障點,當(dāng)前線程阻塞進入休眠狀態(tài)
//在timeout指定的超時時間內(nèi),等待其他參與線程到達屏障點
//如果超出指定的等待時間,則拋出TimeoutException異常,如果該時間小于等于零,則此方法根本不會等待
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
    return dowait(true, unit.toNanos(timeout));
}

private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
    //使用獨占資源鎖控制多線程并發(fā)進入這段代碼
    final ReentrantLock lock = this.lock;
    //獨占鎖控制線程并發(fā)訪問
    lock.lock();
    try {
        final Generation g = generation;

        if (g.broken)
            throw new BrokenBarrierException();
        //如果線程中斷,則喚醒所有等待線程
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
        //每調(diào)用一次await()方法,計數(shù)器就減一
        int index = --count;
        //當(dāng)計數(shù)器值等于0的時
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                //如果在創(chuàng)建CyclicBarrier實例時設(shè)置了barrierAction,則先執(zhí)行barrierAction
                if (command != null)
                    command.run();
                ranAction = true;
                //當(dāng)所有參與的線程都到達屏障點,為喚醒所有處于休眠狀態(tài)的線程做準(zhǔn)備工作
                //需要注意的是,喚醒所有阻塞線程不是在這里
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                if (!timed)
                    //讓當(dāng)前執(zhí)行的線程阻塞,處于休眠狀態(tài)
                    trip.await();
                else if (nanos > 0L)
                    //讓當(dāng)前執(zhí)行的線程阻塞,在超時時間內(nèi)處于休眠狀態(tài)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }

            if (g.broken)
                throw new BrokenBarrierException();

            if (g != generation)
                return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            //釋放獨占鎖
            lock.unlock();
        }
    }
    
    private void nextGeneration() {
        //為喚醒所有處于休眠狀態(tài)的線程做準(zhǔn)備工作
        trip.signalAll();
        //重置count值為parties
        count = parties;
        //重置中斷狀態(tài)為false
        generation = new Generation();
    }

    private void breakBarrier() {
        //重置中斷狀態(tài)為true
        generation.broken = true;
        //重置count值為parties
        count = parties;
        //為喚醒所有處于休眠狀態(tài)的線程做準(zhǔn)備工作
        trip.signalAll();
    }

到這里CyclicBarrier的實現(xiàn)原理基本已經(jīng)都清楚了,下面來深入源碼分析一下線程阻塞代碼trip.await()和線程喚醒trip.signalAll()的實現(xiàn)。

//await()是AQS內(nèi)部類ConditionObject中的方法
public final void await() throws InterruptedException {
//如果線程中斷拋異常
if (Thread.interrupted())
    throw new InterruptedException();
//新建Node節(jié)點,并將新節(jié)點加入到Condition等待隊列中
//Condition等待隊列是AQS內(nèi)部類ConditionObject實現(xiàn)的,ConditionObject有兩個屬性,分別是firstWaiter和lastWaiter,都是Node類型
//firstWaiter和lastWaiter分別用于代表Condition等待隊列的頭結(jié)點和尾節(jié)點
Node node = addConditionWaiter();
//釋放獨占鎖,讓其它線程可以獲取到dowait()方法中的獨占鎖
int savedState = fullyRelease(node);
int interruptMode = 0;
//檢測此節(jié)點是否在資源等待隊列(AQS同步隊列)中,
//如果不在,說明此線程還沒有競爭資源鎖的權(quán)利,此線程繼續(xù)阻塞,直到檢測到此節(jié)點在資源等待隊列上(AQS同步隊列)中
//這里出現(xiàn)了兩個等待隊列,分別是Condition等待隊列和AQS資源鎖等待隊列(或者說是同步隊列)
//Condition等待隊列是等待被喚醒的線程隊列,AQS資源鎖等待隊列是等待獲取資源鎖的隊列
while (!isOnSyncQueue(node)) {
    //阻塞當(dāng)前線程,當(dāng)前線程進入休眠狀態(tài),可以看到這里使用LockSupport.park阻塞當(dāng)前線程
    LockSupport.park(this);
    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
        break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
    unlinkCancelledWaiters();
if (interruptMode != 0)
    reportInterruptAfterWait(interruptMode);
}

//addConditionWaiter()是AQS內(nèi)部類ConditionObject中的方法
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 將condition等待隊列中,節(jié)點狀態(tài)不是CONDITION的節(jié)點,從condition等待隊列中移除
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    //以下操作是用此線程構(gòu)造一個節(jié)點,并將之加入到condition等待隊列尾部
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

//signalAll是AQS內(nèi)部類ConditionObject中的方法
public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //Condition等待隊列的頭結(jié)點
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}

private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
    Node next = first.nextWaiter;
    first.nextWaiter = null;
    //將Condition等待隊列中的Node節(jié)點按之前順序都轉(zhuǎn)移到了AQS同步隊列中
    transferForSignal(first);
    first = next;
} while (first != null);
}

final boolean transferForSignal(Node node) {
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
        //這里將Condition等待隊列中的Node節(jié)點插入到AQS同步隊列的尾部
            Node p = enq(node);
            int ws = p.waitStatus;
            if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
               LockSupport.unpark(node.thread);
            return true;
       }

       //ReentrantLock#unlock()方法
       public void unlock() {
           //Sync是ReentrantLock的內(nèi)部類,繼承自AbstractQueuedSynchronizer,它是ReentrantLock中公平鎖和非公平鎖的基礎(chǔ)實現(xiàn)
           sync.release(1);
       }

       public final boolean release(int arg) {
           //釋放鎖
           if (tryRelease(arg)) {
               //AQS同步隊列頭結(jié)點
               Node h = head;
               if (h != null && h.waitStatus != 0)
                   //喚醒節(jié)點中的線程
                   unparkSuccessor(h);
               return true;
           }
           return false;
       }

       private void unparkSuccessor(Node node) {
           int ws = node.waitStatus;
           if (ws < 0)
               compareAndSetWaitStatus(node, ws, 0);
           Node s = node.next;
           if (s == null || s.waitStatus > 0) {
               s = null;
               for (Node t = tail; t != null && t != node; t = t.prev)
                   if (t.waitStatus <= 0)
                       s = t;
           }
           if (s != null)
               //喚醒阻塞線程
               LockSupport.unpark(s.thread);
    }

原理總結(jié)

CyclicBarrier簡單使用樣例

public class CyclicBarrierDemo {

    @Test
    public void test() {
        final CyclicBarrier barrier = new CyclicBarrier(2, myThread);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName());
                    barrier.await();
                    System.out.println(Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "thread1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName());
                    barrier.await();
                    System.out.println(Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, "thread2").start();
    }

    Thread myThread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("myThread");
        }
    }, "thread3");
}

結(jié)果輸出:

thread1
thread2
myThread
thread2
thread1

用上面的示例總結(jié)一下CyclicBarrier的await方法實現(xiàn)。

假設(shè)線程thread1和線程thread2都執(zhí)行到CyclicBarrier的await(),都進入dowait(boolean timed, long nanos),thread1先獲取到獨占鎖,執(zhí)行到 --count 的時,index等于1,所以進入下面的for循環(huán),接著執(zhí)行trip.await(),進入await()方法,執(zhí)行Node node = addConditionWaiter() 將當(dāng)前線程構(gòu)造成Node節(jié)點并加入到 Condition 等待隊列中,然后釋放獲取到的獨占鎖,當(dāng)前線程進入阻塞狀態(tài)。

此時,線程thread2可以獲取獨占鎖,繼續(xù)執(zhí)行--count,index等于0,所以先執(zhí)行command.run(),輸出myThread,然后執(zhí)行nextGeneration(),nextGeneration()中 trip.signalAll() 只是將Condition等待隊列中的Node節(jié)點按之前順序都轉(zhuǎn)移到了AQS同步隊列中,這里也就是將thread1對應(yīng)的Node節(jié)點轉(zhuǎn)移到了AQS同步隊列中,thread2執(zhí)行完nextGeneration(),返回return 0之前,細看代碼還需要執(zhí)行l(wèi)ock.unlock(),這里會執(zhí)行到ReentrantLock的unlock()方法,最終執(zhí)行到AQS的unparkSuccessor(Node node)方法,從AQS同步隊列中的頭結(jié)點開始釋放節(jié)點,喚醒節(jié)點對應(yīng)的線程,即thread1恢復(fù)執(zhí)行。

如果有三個線程thread1、thread2和thread3,假設(shè)線程執(zhí)行順序是thread1、thread2、thread3,那么thread1、thread2對應(yīng)的Node節(jié)點會被加入到Condition等待隊列中。

當(dāng)thread3執(zhí)行的時候,會將thread1、thread2對應(yīng)的Node節(jié)點按thread1、thread2順序轉(zhuǎn)移到AQS同步隊列中,thread3執(zhí)行l(wèi)ock.unlock()的時候,會先喚醒thread1,thread1恢復(fù)繼續(xù)執(zhí)行,thread1執(zhí)行到lock.unlock()的時候會喚醒thread2恢復(fù)執(zhí)行。

CountdownLatch和CyclicBarrier,哪個可以復(fù)用,為什么

CyclicBarrier可以復(fù)用。

CyclicBarrier設(shè)計成可復(fù)用是因為它內(nèi)部維護了一個“generation”計數(shù)器,這使得即使所有線程通過了一次屏障,CyclicBarrier也可以準(zhǔn)備下一次的屏障。

如果在某個時刻線程因為異常或其他原因沒有成功通過屏障,CyclicBarrier可以通過調(diào)用reset()方法重置狀態(tài),這會清除所有等待線程的狀態(tài),并允許新的線程組再次使用同一個CyclicBarrier實例。

源碼部分

public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier();   // break the current generation
        nextGeneration(); // start a new generation
    } finally {
        lock.unlock();
    }
}

private void breakBarrier() {
    generation.broken = true;
    count = parties;
    trip.signalAll();
}

private void nextGeneration() {
    // signal completion of last generation
    trip.signalAll();
    // set up next generation
    count = parties;
    generation = new Generation();
}

多線程在實際應(yīng)用的場景

  • 并發(fā)處理和并行計算:在Web服務(wù)器、數(shù)據(jù)庫服務(wù)器或任何網(wǎng)絡(luò)服務(wù)器中,多線程可以同時處理多個客戶端的請求,提高服務(wù)器的吞吐量和響應(yīng)能力。并且,在執(zhí)行大量的獨立任務(wù)時,如圖像處理、數(shù)據(jù)清洗、報表生成等,可以將任務(wù)分解并分配給多個線程并行處理,加速處理過程。
  • 異步操作和事件驅(qū)動:在圖形用戶界面中,多線程可以用來處理耗時的后臺任務(wù),如文件讀寫、網(wǎng)絡(luò)請求等,以避免阻塞UI線程,確保界面的響應(yīng)性。并且,使用多線程處理網(wǎng)絡(luò)請求和響應(yīng),可以實現(xiàn)非阻塞I/O,提高網(wǎng)絡(luò)應(yīng)用的效率。
  • 定時任務(wù)和后臺服務(wù):使用多線程實現(xiàn)定時任務(wù),如定期備份、日志輪轉(zhuǎn)、系統(tǒng)監(jiān)控等。或者做如郵件發(fā)送、日志記錄、數(shù)據(jù)分析等長時間運行的服務(wù),可以獨立于主程序線程執(zhí)行。
  • 分布式計算和云計算:在大數(shù)據(jù)處理中,多線程或多進程可以并行執(zhí)行Map和Reduce操作,提高數(shù)據(jù)處理速度。在微服務(wù)架構(gòu)中,每個服務(wù)可以獨立運行在自己的線程或進程中,提高系統(tǒng)的并發(fā)能力和容錯性。

寫過SpringBoot starter嗎

步驟1: 創(chuàng)建Maven項目

首先,需要創(chuàng)建一個新的Maven項目。在pom.xml中添加Spring Boot的starter parent和一些必要的依賴。例如:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

步驟2: 添加自動配置

在src/main/resources/META-INF/spring.factories中添加自動配置的元數(shù)據(jù)。例如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.example.starter.MyAutoConfiguration

然后,創(chuàng)建MyAutoConfiguration類,該類需要@Configuration和@EnableConfigurationProperties注解。@EnableConfigurationProperties用于啟用你定義的配置屬性類。

@Configuration
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {

    @Autowired
    private MyProperties properties;

    @Bean
    public MyService myService() {
        return new MyServiceImpl(properties);
    }
}

步驟3: 創(chuàng)建配置屬性類

創(chuàng)建一個配置屬性類,使用@ConfigurationProperties注解來綁定配置文件中的屬性。

@ConfigurationProperties(prefix = "my")
public class MyProperties {
    private String name;
    // getters and setters
}

步驟4: 創(chuàng)建服務(wù)和控制器

創(chuàng)建一個服務(wù)類和服務(wù)實現(xiàn)類,以及一個控制器來展示你的starter的功能。

@Service
public interface MyService {
    String getName();
}

@Service
public class MyServiceImpl implements MyService {
    private final MyProperties properties;

    public MyServiceImpl(MyProperties properties) {
        this.properties = properties;
    }

    @Override
    public String getName() {
        return properties.getName();
    }
}

@RestController
public class MyController {
    private final MyService myService;

    public MyController(MyService myService) {
        this.myService = myService;
    }

    @GetMapping("/name")
    public String getName() {
        return myService.getName();
    }
}

步驟5: 發(fā)布Starter

將你的starter發(fā)布到Maven倉庫,無論是私有的還是公共的,如Nexus或Maven Central。

步驟6: 使用Starter

在你的主應(yīng)用的pom.xml中添加你的starter依賴,然后在application.yml或application.properties中配置你的屬性。

my:
    name: Hello World

Bean的生命周期

圖片圖片

  1. Spring啟動,查找并加載需要被Spring管理的bean,進行Bean的實例化
  2. Bean實例化后對將Bean的引入和值注入到Bean的屬性中
  3. 如果Bean實現(xiàn)了BeanNameAware接口的話,Spring將Bean的Id傳遞給setBeanName()方法
  4. 如果Bean實現(xiàn)了BeanFactoryAware接口的話,Spring將調(diào)用setBeanFactory()方法,將BeanFactory容器實例傳入
  5. 如果Bean實現(xiàn)了ApplicationContextAware接口的話,Spring將調(diào)用Bean的setApplicationContext()方法,將bean所在應(yīng)用上下文引用傳入進來。
  6. 如果Bean實現(xiàn)了BeanPostProcessor接口,Spring就將調(diào)用他們的postProcessBeforeInitialization()方法。
  7. 如果Bean 實現(xiàn)了InitializingBean接口,Spring將調(diào)用他們的afterPropertiesSet()方法。類似的,如果bean使用init-method聲明了初始化方法,該方法也會被調(diào)用
  8. 如果Bean 實現(xiàn)了BeanPostProcessor接口,Spring就將調(diào)用他們的postProcessAfterInitialization()方法。
  9. 此時,Bean已經(jīng)準(zhǔn)備就緒,可以被應(yīng)用程序使用了。他們將一直駐留在應(yīng)用上下文中,直到應(yīng)用上下文被銷毀。
  10. 如果bean實現(xiàn)了DisposableBean接口,Spring將調(diào)用它的destory()接口方法,同樣,如果bean使用了destory-method 聲明銷毀方法,該方法也會被調(diào)用。

MySQL

MySQL索引什么時候回表

在MySQL中,回表是指在使用非聚集索引(也稱為二級索引或輔助索引)進行查詢時,需要額外訪問主鍵索引(聚集索引)以獲取完整的行數(shù)據(jù)的過程。這是因為非聚集索引中通常只存儲了索引列的值和指向主鍵的引用,而不包含完整的行數(shù)據(jù)。

回表通常在以下情況發(fā)生:

  • 非覆蓋索引查詢:當(dāng)查詢語句所請求的數(shù)據(jù)列不在非聚集索引中,而是需要從主鍵索引中獲取時,就會發(fā)生回表。例如,如果查詢SELECT * FROM table WHERE column1 = value;,且column1是非聚集索引,那么為了返回所有列,MySQL需要根據(jù)column1索引中的主鍵引用回到主鍵索引中獲取完整的行數(shù)據(jù)。
  • 索引列不完全匹配查詢條件:如果查詢條件涉及到多個列,而索引僅包含其中的一部分列,那么MySQL也需要回表來獲取缺失列的數(shù)據(jù)。
  • ORDER BY 或 LIMIT 子句:即使在一個非聚集索引上進行查詢,如果查詢中包含了ORDER BY子句或LIMIT子句,并且排序或限制的依據(jù)不是索引中的列,也可能導(dǎo)致回表。

MySQL執(zhí)行update語句后,主鍵索引、輔助索引、聯(lián)合索引,數(shù)據(jù)都是怎么變的

主鍵索引的變化

主鍵索引通常是聚集索引,在InnoDB中,數(shù)據(jù)行實際上是存儲在主鍵索引的B+樹結(jié)構(gòu)中的葉子節(jié)點上的。這意味著數(shù)據(jù)行的物理位置是由主鍵值確定的。當(dāng)你執(zhí)行一個更新操作時:

  • 如果更新的是非主鍵列,那么InnoDB會更新主鍵索引中對應(yīng)行的非主鍵列數(shù)據(jù),但主鍵值不變,所以行的物理位置也不會改變。
  • 如果更新的是主鍵列,那么InnoDB需要移動數(shù)據(jù)行到新的物理位置,因為主鍵索引決定了數(shù)據(jù)行的位置。這會導(dǎo)致整個數(shù)據(jù)行被移動,并且所有的輔助索引中指向該行的指針也需要更新,以指向新的主鍵值。

輔助索引的變化

輔助索引(也稱非聚集索引或二級索引)在InnoDB中包含兩部分信息:索引列的值和主鍵值(或主鍵的一部分,取決于索引設(shè)計)。更新操作對輔助索引的影響如下:

  • 更新索引列:如果更新的是輔助索引所包含的列,那么InnoDB會更新該索引中的值。如果更新后的值已經(jīng)存在于索引中,那么可能會觸發(fā)索引的重復(fù)檢查,這在唯一索引中尤為關(guān)鍵。
  • 更新主鍵列:如果更新操作改變了主鍵的值,那么所有相關(guān)的輔助索引中的主鍵值都需要更新,以指向新的主鍵值。

聯(lián)合索引的變化

聯(lián)合索引是包含多個列的索引。更新操作對聯(lián)合索引的影響取決于更新的是哪些列:

  • 更新索引內(nèi)的列:如果更新的是聯(lián)合索引中的任何一列,那么InnoDB會更新該索引條目中的相應(yīng)值。
  • 更新主鍵列:如果更新的是主鍵,那么聯(lián)合索引中指向該行的主鍵值也需要更新。

MVCC的實現(xiàn)

MVCC允許多個事務(wù)同時讀取同一行數(shù)據(jù),而不會彼此阻塞,每個事務(wù)看到的數(shù)據(jù)版本是該事務(wù)開始時的數(shù)據(jù)版本。這意味著,如果其他事務(wù)在此期間修改了數(shù)據(jù),正在運行的事務(wù)仍然看到的是它開始時的數(shù)據(jù)狀態(tài),從而實現(xiàn)了非阻塞讀操作。

對于「讀提交」和「可重復(fù)讀」隔離級別的事務(wù)來說,它們是通過 Read View 來實現(xiàn)的,它們的區(qū)別在于創(chuàng)建 Read View 的時機不同,大家可以把 Read View 理解成一個數(shù)據(jù)快照,就像相機拍照那樣,定格某一時刻的風(fēng)景。

  • 「讀提交」隔離級別是在「每個select語句執(zhí)行前」都會重新生成一個 Read View;
  • 「可重復(fù)讀」隔離級別是執(zhí)行第一條select時,生成一個 Read View,然后整個事務(wù)期間都在用這個 Read View。

Read View 有四個重要的字段:

圖片圖片

  • m_ids :指的是在創(chuàng)建 Read View 時,當(dāng)前數(shù)據(jù)庫中「活躍事務(wù)」的事務(wù) id 列表,注意是一個列表,“活躍事務(wù)”指的就是,啟動了但還沒提交的事務(wù)。
  • min_trx_id :指的是在創(chuàng)建 Read View 時,當(dāng)前數(shù)據(jù)庫中「活躍事務(wù)」中事務(wù) id 最小的事務(wù),也就是 m_ids 的最小值。
  • max_trx_id :這個并不是 m_ids 的最大值,而是創(chuàng)建 Read View 時當(dāng)前數(shù)據(jù)庫中應(yīng)該給下一個事務(wù)的 id 值,也就是全局事務(wù)中最大的事務(wù) id 值 + 1;
  • creator_trx_id :指的是創(chuàng)建該 Read View 的事務(wù)的事務(wù) id。

對于使用 InnoDB 存儲引擎的數(shù)據(jù)庫表,它的聚簇索引記錄中都包含下面兩個隱藏列:

圖片圖片

  • trx_id,當(dāng)一個事務(wù)對某條聚簇索引記錄進行改動時,就會把該事務(wù)的事務(wù) id 記錄在 trx_id 隱藏列里;
  • roll_pointer,每次對某條聚簇索引記錄進行改動時,都會把舊版本的記錄寫入到 undo 日志中,然后這個隱藏列是個指針,指向每一個舊版本記錄,于是就可以通過它找到修改前的記錄。

在創(chuàng)建 Read View 后,我們可以將記錄中的 trx_id 劃分這三種情況:

圖片圖片

一個事務(wù)去訪問記錄的時候,除了自己的更新記錄總是可見之外,還有這幾種情況:

  • 如果記錄的 trx_id 值小于 Read View 中的 min_trx_id 值,表示這個版本的記錄是在創(chuàng)建 Read View 前已經(jīng)提交的事務(wù)生成的,所以該版本的記錄對當(dāng)前事務(wù)可見。
  • 如果記錄的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示這個版本的記錄是在創(chuàng)建 Read View 后才啟動的事務(wù)生成的,所以該版本的記錄對當(dāng)前事務(wù)不可見。
  • 如果記錄的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之間,需要判斷 trx_id 是否在 m_ids 列表中:

如果記錄的 trx_id 在 m_ids 列表中,表示生成該版本記錄的活躍事務(wù)依然活躍著(還沒提交事務(wù)),所以該版本的記錄對當(dāng)前事務(wù)不可見。

如果記錄的 trx_id 不在 m_ids列表中,表示生成該版本記錄的活躍事務(wù)已經(jīng)被提交,所以該版本的記錄對當(dāng)前事務(wù)可見。

這種通過「版本鏈」來控制并發(fā)事務(wù)訪問同一個記錄時的行為就叫 MVCC(多版本并發(fā)控制)。

項目

  • 分庫分表是怎么做的,熱點問題怎么解決

算法

旋轉(zhuǎn)鏈表

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if (k == 0 || head == null || head.next == null) {
            return head;
        }
        int n = 1;
        ListNode iter = head;
        while (iter.next != null) {
            iter = iter.next;
            n++;
        }
        int add = n - k % n;
        if (add == n) {
            return head;
        }
        iter.next = head;
        while (add-- > 0) {
            iter = iter.next;
        }
        ListNode ret = iter.next;
        iter.next = null;
        return ret;
    }
}

責(zé)任編輯:武曉燕 來源: 小林coding
相關(guān)推薦

2020-09-03 07:55:02

并發(fā)操作系統(tǒng)Java

2018-01-03 10:34:20

創(chuàng)業(yè)公司事業(yè)

2022-05-27 12:20:18

微信智能手機

2020-07-07 09:19:01

LombokJava IDE

2013-07-17 09:13:19

2021-05-18 06:55:07

客戶端HTML容器

2020-04-22 15:00:03

iPhone蘋果劉海

2018-05-04 10:45:58

戴爾

2017-10-30 12:00:05

2018-10-20 10:30:30

WindowsWindows 10Windows 10.

2019-06-27 22:23:56

谷歌Android開發(fā)者

2025-06-24 10:00:00

智能指針代碼unique_ptr

2023-01-19 00:13:28

代碼技術(shù)計劃

2023-04-20 17:51:41

馬斯克微軟

2022-01-09 23:44:14

5G4G手機

2016-01-08 10:32:48

虛擬現(xiàn)實頭盔

2020-03-13 09:38:45

離職創(chuàng)業(yè)感受

2017-11-20 09:00:43

跳槽職場精英年終獎

2020-09-27 14:13:50

Spring BootJava框架

2021-03-01 09:16:10

程序員系統(tǒng)模式
點贊
收藏

51CTO技術(shù)棧公眾號

夜夜嗨aⅴ一区二区三区| a视频免费观看| 丝袜国产在线| av高清不卡在线| 国产精品电影一区| 91香蕉视频在线播放| 国产伦精品一区二区三区在线播放| 激情成人在线视频| 中文字幕剧情在线观看一区| 人妻偷人精品一区二区三区| 免费观看久久久4p| 欧美大片在线看免费观看| 99re久久精品国产| 国产精品99久久免费| 日韩欧美国产激情| 日韩专区第三页| 91精品国产综合久久久久久豆腐| 国产成人免费在线观看不卡| 国产精品久在线观看| 日韩高清精品免费观看| 久久久久久久久久久9不雅视频| 亚洲黄色av女优在线观看| 亚洲精品国产一区二区三区| 三上悠亚一区二区| 黄色91在线观看| 人人妻人人澡人人爽欧美一区| 国产中文在线视频| 99这里都是精品| 91精品久久久久久蜜桃| 中日精品一色哟哟| 噜噜噜在线观看免费视频日韩| 久热精品视频在线| 国精产品视频一二二区| 国产成人黄色| 国产偷亚洲偷欧美偷精品| 国产人妻精品午夜福利免费| 9999精品| 欧美日韩国产在线播放网站| 密臀av一区二区三区| 日本不卡网站| 天天av天天翘天天综合网色鬼国产| 欧美一区二区三区综合| dj大片免费在线观看| 国产精品久久久久久久久果冻传媒| 欧美一区二区综合| 欧美日本网站| 久久久久久久久久久黄色| 久久精品国产一区二区三区日韩| 黄色av免费观看| 成人午夜精品在线| 91精品国产一区二区三区动漫| 国产999久久久| 国产一区二区三区免费在线观看| 成人黄色免费看| ,一级淫片a看免费| 久久99久国产精品黄毛片色诱| 国产精品亚洲一区二区三区| 一级黄色免费片| 久久国产精品99久久久久久老狼 | 国产成人在线视频网站| 亚洲综合日韩中文字幕v在线| 91极品身材尤物theporn| 久草在线在线精品观看| 91系列在线播放| 精品人妻aV中文字幕乱码色欲| 国产成人精品www牛牛影视| 91丝袜脚交足在线播放| 亚洲高清精品视频| av电影在线观看完整版一区二区| 九色综合日本| 成人性生交大片免费看午夜 | 精品第一国产综合精品aⅴ| 老熟女高潮一区二区三区| 激情亚洲另类图片区小说区| 亚洲欧美国产制服动漫| 亚洲一级理论片| 91精品久久久久久久久久不卡| 美女福利精品视频| 国产系列精品av| 久久香蕉精品| 91久久久在线| 天天操天天干天天舔| 久久影院午夜论| 一区二区三区国产福利| xxxcom在线观看| 色网综合在线观看| 一级做a爱视频| 日本福利一区| 久久精品一本久久99精品| 精品少妇theporn| 日韩精品久久理论片| 91网在线免费观看| 天堂av在线资源| 国产午夜精品一区二区三区四区| 中文字幕在线中文字幕日亚韩一区 | 日本久久高清视频| 蜜桃av在线播放| 7777精品伊人久久久大香线蕉完整版 | 99精品视频一区二区| 亚洲精品日韩在线观看| 牛牛精品视频在线| 在线观看视频91| 色哟哟无码精品一区二区三区| 国产探花在线精品| 国内精品久久久久久| 国产精品久久久久久久久久久久久久久久 | 四虎精品成人影院观看地址| 中文天堂在线一区| 欧美色图色综合| 欧美经典一区| 色噜噜狠狠色综合网图区| 在线观看免费国产视频| 国产麻豆视频一区| 亚洲黄色成人久久久| 一个人看的www视频在线免费观看| 777a∨成人精品桃花网| 欧美a在线播放| 国产精品毛片在线| 国产精品久久久久av福利动漫| 91精品国产91久久久久游泳池 | 91视频这里只有精品| 日韩电影在线观看完整免费观看| 欧美成人免费网| 亚洲天天综合网| 国产欧美一区二区精品忘忧草 | 四虎影视4hu4虎成人| 亚洲高清色综合| 五月天丁香激情| 精品一区精品二区高清| 亚洲v日韩v欧美v综合| 成年美女黄网站色大片不卡| 欧美精品一区二区三区蜜桃| 老熟妻内射精品一区| 久久av资源网| 一级二级三级欧美| 成人国产精选| 中文字幕少妇一区二区三区| jizz国产在线| 久久久久久久国产精品影院| 欧美在线观看成人| 日韩av网站在线免费观看| 久久久亚洲精选| 日批视频在线播放| 亚洲mv大片欧洲mv大片精品| 在线观看一区二区三区四区| 激情文学一区| 国产在线播放一区二区| cao在线视频| 日韩欧美一级在线播放| 麻豆changesxxx国产| 国产成人综合网| 可以看毛片的网址| 欧美日韩破处| 日韩女优在线播放| 成人网视频在线观看| 欧美网站大全在线观看| www中文在线| 国产精品资源在线观看| 很污的网站在线观看| 第四色中文综合网| 国产91精品久久久| 成人三级黄色免费网站| 欧美电影一区二区| 青青操国产视频| 99久久精品久久久久久清纯| 欧美韩国日本在线| 青青草成人影院| 亚洲精品欧美一区二区三区| a毛片不卡免费看片| 亚洲精品mp4| 久久久久久久久久一级| 亚洲男人的天堂在线aⅴ视频 | 成人在线tv视频| 68精品久久久久久欧美| 福利成人在线观看| 777精品伊人久久久久大香线蕉| 免费在线看黄网址| 久久综合精品国产一区二区三区| 一级黄色香蕉视频| 午夜视频精品| 久久艳妇乳肉豪妇荡乳av| 草民电影神马电影一区二区| 九九久久精品一区| 日本一区二区三区在线观看视频| 欧美三级在线看| 精品视频一区二区在线观看| wwwwxxxxx欧美| www.久久久久久久久久久| a91a精品视频在线观看| 亚洲欧美综合一区| 999久久久久久久久6666| 日本视频久久久| 91麻豆一二三四在线| 日韩精品www| jlzzjlzz亚洲女人18| 疯狂蹂躏欧美一区二区精品| 欧美视频www| 久久久久青草大香线综合精品| 肉色超薄丝袜脚交| 三级成人在线视频| 欧美久久在线观看| 亚洲a在线视频| 日本不卡久久| 高清日韩中文字幕| 成人激情黄色网| 欧美精品高清| 久久免费视频观看| 黄视频网站在线| 一区二区三区天堂av| 日本美女一级视频| 日韩一区二区在线观看| 成人小视频在线播放| 性做久久久久久久免费看| 日本美女黄色一级片| 久久精品人人做人人综合 | 欧美精品一区二区三区国产精品| 国产毛片av在线| 亚洲黄色免费三级| www.97av| 欧美一级欧美一级在线播放| 中文字幕av网站| 色综合天天综合色综合av| 精品午夜福利在线观看| 亚洲欧洲日本在线| 亚洲一级片在线播放| 91美女精品福利| 一区二区免费在线观看视频| 国产精品99久久久久| 91精品视频国产| 久久99久久久欧美国产| 一区二区免费av| 毛片av一区二区三区| 午夜免费一区二区| 老司机精品福利视频| 国产精品亚洲a| 免费亚洲网站| www.浪潮av.com| 夜夜嗨一区二区| av免费观看大全| 日韩视频二区| 黄色一级在线视频| 国产欧美成人| av免费中文字幕| 麻豆精品网站| 毛葺葺老太做受视频| 日韩在线一二三区| wwww.国产| 精品一区二区三区免费毛片爱 | 黄色在线免费观看| 色综合久久综合中文综合网| 五月天婷婷激情| 91黄色免费网站| 在线观看中文字幕av| 欧美日韩五月天| 亚洲天堂avav| 91精品国产色综合久久| 亚洲精品成人电影| 亚洲第一中文字幕| 欧美成人免费| 自拍偷拍亚洲一区| 国产激情视频在线观看| 久久久久久久一区二区三区| 色戒汤唯在线观看| 国产精品久久久久久av福利软件| 不卡亚洲精品| 成人情视频高清免费观看电影| 凹凸av导航大全精品| 久久资源亚洲| 91欧美在线| 男人添女荫道口喷水视频| 国产视频一区欧美| 五月婷婷之婷婷| 岛国av在线一区| 少妇大叫太粗太大爽一区二区| 欧美高清在线视频| 免费在线观看日韩| 日本韩国欧美在线| 国产高潮流白浆喷水视频| 亚洲国产精品久久久久久| 国产美女性感在线观看懂色av| 久久天天躁狠狠躁夜夜躁2014| 国产嫩草在线视频| 国产精品黄色av| 中文字幕一区二区三区日韩精品 | 天堂美国久久| 国产一区二区网| 久久福利资源站| 亚洲最大免费视频| 成人免费小视频| 亚洲天堂av片| 日韩免费一区二区三区在线播放| 天堂中文在线视频| 美女国内精品自产拍在线播放| 免费看男女www网站入口在线| 国产精品自在线| 欧美在线关看| 无码人妻aⅴ一区二区三区日本| 国产欧美一级| 潘金莲一级淫片aaaaaaa| 欧美激情在线免费观看| 国产午夜精品无码| 欧美日本韩国一区| 九色视频在线播放| 97人人爽人人喊人人模波多 | 欧美日韩黄色网| 色噜噜狠狠色综合欧洲selulu| 亚洲成人77777| 色妞欧美日韩在线| 欧美三级网址| 精品欧美国产一区二区三区不卡| 88国产精品视频一区二区三区| 国产男女无遮挡| 成人综合在线观看| 伊人久久久久久久久久久久久久| 色综合婷婷久久| 色综合视频在线| 欧美另类交人妖| 亚洲国产综合在线观看| 日本一区二区视频| 国产免费成人| 中文字幕在线视频播放| 一区二区三区日韩欧美精品 | 成人在线网站| 蜜桃视频日韩| 在线一区欧美| 艳妇乳肉豪妇荡乳xxx| 亚洲在线一区二区三区| 国产区精品在线| 精品国产网站地址| 国产成人精品一区二区三区免费| 欧美日韩系列| 久久久久网站| 波多野结衣福利| 欧美日韩中文在线| 日韩私人影院| 日韩免费观看av| 国产a久久精品一区二区三区| 日本免费黄视频| 久久综合九色综合97婷婷女人| 日韩 欧美 中文| 亚洲国产欧美久久| 岛国av免费在线观看| 九九九久久久| 国产精品色网| 国产一区二区三区四区五区六区| 色琪琪一区二区三区亚洲区| 久久久久久女乱国产| 国产精品高潮呻吟久久av野狼| 国产成人精品免费视| 天天操,天天操| 亚洲欧美色图小说| 午夜精品久久久久久久91蜜桃| 欧美极品xxxx| 日韩深夜影院| 男女男精品视频站| 国产精品蜜臀av| 国产av无码专区亚洲av| 欧美极品少妇与黑人| 日本韩国欧美超级黄在线观看| 日韩欧美精品在线观看视频| 久久久99精品久久| 狠狠人妻久久久久久| 在线观看日韩av| 中文字幕成人| 菠萝蜜视频在线观看入口| 丁香亚洲综合激情啪啪综合| 国产精品自拍99| 一区二区成人精品| 精品视频一区二区三区| 欧美乱大交xxxxx潮喷l头像| 久久免费的精品国产v∧| 中文字幕免费观看视频| 欧美疯狂做受xxxx高潮| 丝袜美腿一区二区三区动态图 | 在线观看欧美| 日韩精品视频在线观看视频| 久久久久久一级片| 国产又粗又大又爽| 欧美巨乳在线观看| 丝袜美腿一区二区三区动态图| 欧美特级aaa| 午夜欧美大尺度福利影院在线看| 黄色的视频在线免费观看| 92国产精品视频| 午夜亚洲视频| 国产又粗又硬又长又爽| 日韩成人在线观看| 欧美v亚洲v综合v国产v仙踪林| 精品人妻大屁股白浆无码| 久久精品亚洲精品国产欧美 | 亚洲狼人综合| 欧美中日韩在线| 国产精品久久久久久久久免费相片 | 久久国产欧美日韩精品| 日产精品久久久久| 日韩在线观看免费全| 欧美日韩精品一区二区三区在线观看| 香蕉视频999| 日本久久精品电影| 变态调教一区二区三区|