《電子技術應用》
您所在的位置:首頁 > 其他 > 業界動態 > Java 下實現鎖無關數據結構

Java 下實現鎖無關數據結構

2008-07-24
作者:賴 德怡
本文將介紹鎖無關數據結構" title="數據結構">數據結構的應用及其相關概念,并在 Java 環境下利用 JDK 1.5 提供的一組類進行鎖無關數據結構設計,從而避免基于鎖的數據結構可能引發的同步問題" title="同步問題">同步問題,以改善程序的可靠性。

介紹

??? 通常在一個多線程環境下,我們需要共享某些數據,但為了避免競爭條件引致數據出現不一致的情況,某些代碼段需要變成原子操作去執行。這時,我們便需要利用各種同步機制如互斥(Mutex)去為這些代碼段加鎖,讓某一線程可以獨占共享數據,避免競爭條件,確保數據一致性。但可惜的是,這屬于阻塞性同步,所有其他線程唯一可以做的就是等待。基于鎖(Lock based)的多線程設計更可能引發死鎖" title="死鎖">死鎖、優先級倒置、饑餓等情況,令到一些線程無法繼續其進度。

??? 鎖無關(Lock free)算法,顧名思義,即不牽涉鎖的使用。這類算法可以在不使用鎖的情況下同步各個線程。對比基于鎖的多線程設計,鎖無關算法有以下優勢:

  • 對死鎖、優先級倒置等問題免疫:它屬于非阻塞性同步,因為它不使用鎖來協調各個線程,所以對死鎖、優先級倒置等由鎖引起的問題免疫;
  • 保證程序的整體進度:由于鎖無關算法避免了死鎖等情況出現,所以它能確保線程是在運行當中,從而確保程序的整體進度;
  • 性能理想:因為不涉及使用鎖,所以在普遍的負載環境下,使用鎖無關算法可以得到理想的性能提升。

??? 自 JDK 1.5 推出之后,當中的 java.util.concurrent.atomic 的一組類為實現鎖無關算法提供了重要的基礎。本文介紹如何將鎖無關算法應用到基本的數據結構中,去避免競爭條件,允許多個線程同時存取和使用集合中的共享數據。如果一個數據結構本身并非是線程安全的,一旦在多線程環境下使用這個數據結構,必須施加某種同步機制,否則很可能會出現競爭條件。我們即將設計的鎖無關數據結構是線程安全的,所以使用時無需再編寫額外代碼去確保競爭條件不會出現。

數據結構的設計

??? 本文會由淺入深,先提出鎖無關棧(Stack)的實現方法,為讀者提供必須的基礎知識,棧是一個先入后出(Last in first out)的基本數據結構。當讀者掌握必要的技術之后,我們便會著手設計相對復雜的鏈表" title="鏈表">鏈表(Linked List)數據結構,鏈表是很多其他數據結構的基礎組成部分。不過,對比起棧,鏈表可以面對更棘手的線程同步問題。

??? 在開始設計之前,我們需要理解一個十分重要的原語" title="原語">原語 Compare-and-swap (CAS) ,Herlihy 證明了 CAS 是實現鎖無關數據結構的通用原語, CAS 可以原子地比較一個內存位置的內容及一個期望值,如果兩者相同,則用一個指定值取替這個內存位罝里的內容,并且提供結果指示這個操作是否成功。很多現代的處理器已經提供了 CAS 的硬件實現,例如在 x86 架構下的 CMPXCHG8 指令。而在 Java 下,位于 java.util.concurrent.atomic 內的 AtomicReference 類亦提供了 CAS 原語的實現,并且有很多其他的擴展功能。 CAS 操作將會是稍后實現的鎖無關數據算法無可缺少的指令。

??? 棧能以數組或者鏈表作為底下的儲存結構,雖然采取鏈表為基礎的實現方式會占用多一點空間去儲存代表元素的節點,但卻可避免處理數組溢出的問題。故此我們將以鏈表作為棧的基礎。

??? 首先,我們分析一下一個非線程安全的版本。為了清楚表達和集中于文章的主題,代碼沒有包含對異常及不正當操作的處理,讀者請留意。它的代碼如下:


清單 1. 非線程安全的棧實現
                
 class Node { 
    Node next; 
    T value; 
    
    public Node(T value, Node next) { 
        this.next = next; 
        this.value = value; 
    } 
 } 

 public class Stack { 
    Node top; 
    
    public void push(T value) { 
        Node newTop = new Node(value, top); 
        top = newTop; 
    } 
    
    public T pop() { 
        Node node = top; 
        top = top.next; 
        return node.value; 
    } 
    
    public T peek() { 
        return top.value; 
    } 
 } 

??? 數據成員 top 儲存著棧頂的節點,它的類型為 Node ,這是因為我們的棧是基于鏈表的。 Node 代表一個節點,它有兩個數據成員, value 儲存著入棧的元素,而 next 儲存下一個節點。這個類有三個方法,分別是 pushpoppeek ,它們是基本的棧操作。除了 peek 方法是線程安全之外,其余兩個方法在多線程環境之下都有可能引發競爭條件。

push 方法

??? 讓我們先考慮一下 push 方法,它能將一個元素入棧。調用 push 時,它首先建立一個新的節點,并將 value 數據成員設定為傳入的參數,而 next 數據成員則被賦值為當前的棧頂。然后它把 top 數據成員設定成新建立的節點。假設有兩個線程 A 和 B 同時調用 push 方法,線程 A 獲取當前棧頂的節點去建立新的節點( push 方法代碼第一行),但由于時間片用完,線程 A 暫時掛起。此時,線程 B 獲取當前棧頂的節點去建立新的節點,并把 top 設定成新建立的節點( push 方法代碼第二行)。然后,線程 A 恢復執行,更新棧頂。當線程 B 對 push 的調用完成后,線程 A 原本獲得的棧頂已經「過期」,因為線程 B 用新的節點取代了原本的棧頂。

pop 方法

??? 至于 pop 方法,它把棧頂的元素彈出。 pop 方法把棧頂暫存在一個本地變量 node ,然后用下一個節點去更新棧頂,最后返回變量 nodevalue 數據成員。如果兩個線程同時調用這個方法,可能會引起競爭條件。當一個線程將當前棧頂賦值到變量 node ,并準備用下一個節點更新棧頂時,這個線程掛起。另一個線程亦調用 pop 方法,完成并返回結果。剛剛被掛起的線程恢復執行,但由于棧頂被另一個線程變更了,所以繼續執行的話會引起同步問題。

peek 方法

????而 peek 方法只是簡單地返回當前位于棧頂的元素,這個方法是線程安全的,沒有同步問題要解決。

??? 在 Java 要解決 pushpop 方法的同步問題,可以用 synchronized 這個關鍵詞,這是基于鎖的解決方案。現在我們看看鎖無關的解決方案,以下是鎖無關棧實現的代碼:


清單 2. 鎖無關的棧實現
                
 import java.util.concurrent.atomic.*; 

 class Node { 
    Node next; 
    T value; 
    
    public Node(T value, Node next) { 
        this.next = next; 
        this.value = value; 
    } 
 } 

 public class Stack { 
    AtomicReference> top = new AtomicReference>(); 
    
    public void push(T value) { 
        boolean sucessful = false; 
        while (!sucessful) { 
            Node oldTop = top.get(); 
            Node newTop = new Node(value, oldTop); 
            sucessful = top.compareAndSet(oldTop, newTop); 
        }; 
    } 
    
    public T peek() { 
        return top.get().value; 
    } 
    
    public T pop() { 
        boolean sucessful = false; 
        Node newTop = null; 
        Node oldTop = null; 
        while (!sucessful) { 
            oldTop = top.get(); 
            newTop = oldTop.next; 
            sucessful = top.compareAndSet(oldTop, newTop); 
        } 
        return oldTop.value; 
    } 
 } 

??? 這個新的實現方式和剛剛的很不同,看似比較復雜。成員數據 top 的類型由 Node 改為 AtomicReference>AtomicReference 這個類可以對 top 數據成員施加 CAS 操作,亦即是可以允許 top 原子地和一個期望值比較,兩者相同的話便用一個指定值取代。從上文可知,我們需要解決遇到棧頂「過期」的問題。

push 方法

??? 現在我們先分析新的 push 方法如何處理這個問題,確保競爭條件不會出現。在 while 循環中,通過在 top 數據成員調用 AtomicReference.get()oldTop 持有當前棧頂節點,這個棧頂稍后會被取替。變量 newTop 則被初始化為新的節點。最重要的一步, top.compareAndSet(oldTop, newTop) ,它比較 topoldTop 這兩個引用是否相同,去確保 oldTop 持有的棧頂并未「過期」,亦即未被其他線程變更。假如沒有過期,則用 newTop 去更新 top ,使之成為新的棧頂,并返回 booleantrue 。否則, compareAndSet 方法便返回 false ,并且令到循環繼續執行,直至成功。因為 compareAndSet 是原子操作,所以可以保證數據一致。

pop 方法

??? pop 方法把棧頂的元素彈出,它的實現方式和 push 方法十分類同。在 while 循環內, compareAndSet 檢查棧頂有沒有被其他線程改變,數據一致的話便更新 top 數據成員并把原本棧頂彈出。如果失敗,便重新嘗試,直至成功。

pushpop 都沒有使用任何鎖,所以全部線程都不用停下來等待。

鏈表

??? 棧是一個相當簡單的數據結構,要解決的同步問題亦比較直接和容易。但很多環境下,棧并不能滿足我們的需求。我們將介紹鏈表,它有更廣泛的應用范圍。為了保持簡潔,這個鏈表所提供的方法較少。以下是鏈表的非線程安全版本:


清單 3. 非線程安全的鏈表實現
                
 class Node { 
    Node next; 
    T value; 
    
    public Node(T value, Node next) { 
        this.value = value; 
        this.next = next; 
    } 
 } 

 class LinkedList { 
    Node head; 
    
    public LinkedList() { 
        head = new Node(null, null); 
    } 
    
    public void addFirst(T value) { 
        addAfter(head.value, value); 
    } 
    
    public boolean addAfter(T after, T value) { 
        for (Node node = head; node != null; node = node.next) { 
            if (isEqual(node.value, after)) { 
                Node newNode = new Node(value, node.next); 
                node.next = newNode; 
                return true; 
            } 
        } 
        return false; 
    } 
    
    public boolean remove(T value) { 
        for (Node node = head; node.next != null; node = node.next) { 
            if (isEqual(node.next.value, value)) { 
                node.next = node.next.next; 
                return true; 
            } 
        } 
        return false; 
    } 
    
    boolean isEqual(T arg0, T arg1) { 
        if (arg0 == null) { 
            return arg0 == arg1; 
        } else { 
            return arg0.equals(arg1); 
        } 
    } 
 } 

??? 數據成員 head 是鏈表的頭,它沒有存儲任何元素,而是直接指向第一個元素,這可以令稍后的 remove 方法較易實現。這個鏈表有三個公用方法,其中 addAfterremove 比較重要。

addAfter 方法

??? 先考慮一下 addAfter 方法,這個方法把一個新元素加入到集合內指定元素之后的位置,并返回一個 boolean 值指示元素有沒有被加入到集合中,元素沒有被加入的原因是因為集合內沒有所指定的元素。它首先在一個 for 循環中尋找指定元素的節點,成功發現指定的節點后,便建立一個新節點。這個新節點的 next 數據成員連接到指定節點原本的 next ,指定節點的 next 則連到新節點上。另一方面, remove 方法尋找指定元素并從集合中移除,并且返回一個 boolean 值指示元素有沒有被移除,返回 false 代表集合中沒有指定的元素。這個方法在一個循環尋找要移除的元素,并且將左右兩邊的元素重新連接。

??? 在多線程環境下,如果兩個線程同時調用 addAfterremove 方法,或者一個線程調用 addAfter 方法而同一時間另一個線程調用 remove 方法均有機會引發競爭條件。

??? 試想像現在鏈表內有三個元素,分別是 A、B 和 C 。如果一個線程準備把一個元素加到 A 之后,它首先確定 A 節點的位置,然后新建一個節點 A1,這個節點的 value 數據成員儲存著新加入的元素,而 next 數據成員則儲存著 B 節點的引用。當這個線程準備把 A 和 A1 通過 next 成員連接起來時,此時線程因為時間片用完而被掛起。另一個線程亦準備在 A 之后加入一個新元素,它建立 A2 節點,并解除 A 和 B 原本的連結,然后依著 A-A2-B 的次序重新連接三個節點,操作完成。現在,剛剛被掛起的線程恢復執行,并依著 A-A1-B 的次序去重新連接三個節點。這時問題出現,剛剛新加入的 A2 節點遺失了。解決方法是,每一次準備把 A 元素和新建立的節點連接時,檢查 A 節的 next 有否被其他線程改動過,沒有改動過才進行連接,這是通過 CAS 操作原子地進行的。

addAfter 和 remove 的沖突

??? 同時間執行 addAfterremove 亦有可能引起競爭條件。同樣地,現在一個鏈表內有三個元素 A、B 和 C 。當一個線程準備調用 remove 方法從這個集合中移除 B 元素,它首先獲得 A 節點,然后準備通過改變 A 節點的 next 成員,把 A 和 C 互相連接時,這個線程突然被掛起。此時另一個線程調用 addAfter 方法在 B 節點后插入一個新的元素 B2 。插入操作完成后,剛才被掛起的線程恢復執行,并且通過改變 next 成員把 A 和 C 互相連接,完成移除操作。可是,剛剛被加入的 B2 元素則遺失了,因為 A 節點跳過了 B 節點,直接連接著 C 節點。故此,我們要有一個解決方案。 Timothy L. Harris 提供了一個方法,他把整個移除過程分成兩個步驟,邏輯刪除和物理刪除。邏輯刪除并非真正地移除一個節點,而是把要移除的節點標記為已刪除。另一方面,物理刪除則真實從集合左移除一個節點。每次要加入新元素到指定節點之后,都必先檢查該節點有沒有被標記為刪除,沒有的話才把新的節點連接到集合中。這是通過 AtomicMarkableReference 類中的 compareAndSet 方法原子地進行的。

remove 方法

??? 鏈表有可能發生的沖突比較多,另一個問題便是兩個線程同時間執行 remove 方法。這個問題和同時間執行 addAfter 有點類同。現在假設一個集合內有四個元素 A、B、C 和 D,一個線程調用 remove 方法去移除元素 B 。它首先確定了 A 和 C 的位置,然后準備解除 A 和 B 的連結,再將 A 和 C 連接起來,實際的移除還未實行,這時這個線程被掛起了。另一個線程亦調用 remove 方法移除 C 元素,它解除 B 和 C 的連結,并把 B 和 D 連接起來,移除操作完成。之后剛才的線程恢復運行,繼續執行余下的操作,把 A 和 C 連接起來,這樣之前的移除 C 的操作便受到了破壞。最終鏈表中的元素變成 A-C-D,C 元素沒有被移除。所以,我們 remove 方法需要確定要移除的元素的 next 有沒有被改變。例如移除 B 的時候,檢查 A 的 next 有沒有被其他線程更動,以及有沒有被標記為已經邏輯地刪除。這亦是透過 CAS 操作去完成的。

??? 從上文的各種情況可見,我們必須原子地施加某些檢查機制,確保數據的一致性。我們現在看看解決這些問題的鎖無關鏈表是如何實現的,這些代碼應該和讀者在算法書上看到的很不同。以下是它的代碼:


清單 4. 鎖無關的鏈表實現
                
 import java.util.concurrent.atomic.*; 

 class Node { 
    AtomicMarkableReference> next; 
    T value; 
    
    public Node(T value, Node next) { 
        this.next = new AtomicMarkableReference>(next, false); 
        this.value = value; 
    } 
 } 
    
 class LinkedList { 
    AtomicMarkableReference> head; 

    public LinkedList() { 
        Node headNode = new Node(null, null); 
        head = new AtomicMarkableReference>(headNode, false); 
    } 
    
    public void addFirst(T value) { 
        addAfter(head.getReference().value, value); 
    } 
    
    public boolean addAfter(T after, T value) { 
        boolean sucessful = false; 
        while (!sucessful) { 
            boolean found = false; 
            for (Node node = head.getReference(); node != null && !isRemoved(node); 
            node = node.next.getReference()) { 
                if (isEqual(node.value, after) && !node.next.isMarked()) { 
                    found = true; 
                    Node nextNode = node.next.getReference(); 
                    Node newNode = new Node(value, nextNode); 
                    sucessful = node.next.compareAndSet(nextNode, newNode, false, false); 
                    break; 
                } 
            } 
            if (!found) { 
                return false; 
            } 
        } 
        return true; 
    } 
    
    public boolean remove(T value) { 
        boolean sucessful = false; 
        while (!sucessful) { 
            boolean found = false; 
            for (Node node = head.getReference(), nextNode = node.next.getReference(); 
            nextNode != null; node = nextNode, nextNode = nextNode.next.getReference()) { 
                if (!isRemoved(nextNode) && isEqual(nextNode.value, value)) { 
                    found = true; 
                    logicallyRemove(nextNode); 
                    sucessful = physicallyRemove(node, nextNode); 
                    break; 
                } 
            } 
            if (!found) { 
                return false; 
            } 
        } 
        return true; 
    } 
    
    void logicallyRemove(Node node) { 
        while (!node.next.attemptMark(node.next.getReference(), true)) { } 
    } 
    
    boolean physicallyRemove(Node leftNode, Node node) { 
        Node rightNode = node; 
        do { 
            rightNode = rightNode.next.getReference(); 
        } while (rightNode != null && isRemoved(rightNode)); 
        return leftNode.next.compareAndSet(node, rightNode, false, false); 
    } 
    
    boolean isRemoved(Node node) { 
        return node.next.isMarked(); 
    } 
    
    boolean isEqual(T arg0, T arg1) { 
        if (arg0 == null) { 
            return arg0 == arg1; 
        } else { 
            return arg0.equals(arg1); 
        } 
    } 
 } 

??? 和之前不同, Node 類中的 next 成員數據屬于 AtomicMarkableReference 類,不是 Node ,亦不是 AtomicReference 。這是因為我們不但需要在 next 成員進行 CAS 操作,也需要在 next 中加上標記。 AtomicMarkableReference 上的標記是以一個 boolean 表示的。我們會以設定它為 true 來代表一個節點已被邏輯刪除, false 則代表這個節點未被邏輯刪除。當一個節點被標記為已經邏輯地刪除,它的 next 數據成員的標記位會被設定成 booleantrue 。另一方面,鏈表中的 head 亦屬 AtomicMarkableReference 這類型,因為我們也需要進行同樣的操作。

addAfter 方法

??? 首先考慮一下 addAfter 方法, addAfter 方法的設計必須顧慮到兩個線程同時間插入及同時間一個線程插入和一個線程移除的沖突。在一個 for 循環中,它遍歷集合去尋找 after 所位于的節點,并通過調用 getReference()after 的下一個節點賦值到 nextNode 變量中。然后,建立一個新的節點去容納新加入的元素,它通過 next 數據成員和 nextNode 變量進行連接。下一步,在 node 變量上調用 compareAndSet 。不同之處在于它不但比較兩個引用是否相同去確保 next 數據成員沒有被其他線程改變過,它亦會比較 boolean 標記位和期望值是否相同。上文提到,AtomicMarkableReference 類擁有一個標記位,我們利用它去檢查一個節點是否已被邏輯地刪除了。如果我們發現那個節點已被 logicallyRemove 方法標記為已經被邏輯地刪除, compareAndSet 方法便會失敗,并繼續循環尋找下一個符合的節點。若果循環終結后仍未尋找到指定的元素, a ddAfter 方法便會返回 false 以表示由于集合內不存在指定的元素,所以元素無法被加入到集合中。 compareAndSet 返回 true 便代表元素插入成功,方法便會返回并結束。

addFirst 方法

???addFirst 方法只是簡單地調用 addAfter 方法去把一個新的元素插入到集合的開端。

Remove 方法

?? remove 方法在一個循環內尋找要移除元素的前一個節點,然后確定這個節點未被邏輯地移除。確定后,它首先調用 logicallyRemove 邏輯地刪除節點,然后調用 physicallyRemove 物理地刪除節點。在 logicallyRemove 中,我們調用 AtomicMarkableReference 中的 attemptMark 來設定標記位。由于 attemptMark 可能會失敗,所以要將它放進一個 while 循環中,經過 attemptMark 設定標記的節點代表該節點已被邏輯地刪除。在 physicallyRemove 方法中,它首先檢查鄰近的其他節點是否都已經被標記為邏輯刪除,若果已被標記則順道物理地移除它們。這是通過 compareAndSet 完成, compareAndSet 會檢查節點是否已被邏輯地刪除,以及上一個節點的 next 成員未被其他線程更改。這樣可以確保兩個線程同時調用 remove 方法,或者分別同時間調用 remove 方法和 a ddAfter 方法的時候,競爭條件不會發生。

ABA 問題

??? 因為 CAS 操作比較一個內存位置的內容及一個期望值是否相同,但如果一個內存位置的內容由 A 變成 B,再由 B 變成 A, CAS 仍然會看作兩者相同。不過,一些算法因為需求的關系無法容忍這種行為。當一些內存位置被重用的時候,這個問題便可能會發生。在沒有垃圾回收機制的環境下,ABA 問題需要一些機制例如標記去解決。但由于 JVM 會為我們處理內存管理的問題,故此以上的實現足以避免 ABA 問題的出現。

結束語

??? 以往很多的鎖無關數據結構都以 Immutable object 的方式去達致線程安全,這很像 Java 中的 String ,但因為涉及過多的復制操作,令性能低下。但經過十多年,鎖無關數據結構已發展得十分成熟,性能并不遜色于傳統的實現方式。編寫鎖無關算法是十分困難的,但因為數據結構是經常被重用的部分,首先把這個概念應用到數據結構中,可以輕易讓程序進入鎖無關的世界,體驗它所帶來的好處。



參考資料

本站內容除特別聲明的原創文章之外,轉載內容只為傳遞更多信息,并不代表本網站贊同其觀點。轉載的所有的文章、圖片、音/視頻文件等資料的版權歸版權所有權人所有。本站采用的非本站原創文章及圖片等內容無法一一聯系確認版權者。如涉及作品內容、版權和其它問題,請及時通過電子郵件或電話通知我們,以便迅速采取適當措施,避免給雙方造成不必要的經濟損失。聯系電話:010-82306118;郵箱:aet@chinaaet.com。
主站蜘蛛池模板: 国产成人精品一区二三区四区五区 | 91久久北条麻妃一区二区三区 | a天堂资源在线观看 | 国产人妖av | 中文字幕人成乱码在线观看 | 国产资源网 | 国产精品成人免费精品自在线观看 | 国产性av在线 | k8经典少妇在线观看 | 不卡二区| √最新版天堂资源网在线 | 欧美精品播放 | 黄色片视频免费观看 | 欧美精品h| 国内一区二区三区 | 亚洲第一免费 | 久久精品国语 | 亚洲国产精品ⅴa在线观看 天堂中文在线资源 | 叼嘿视频在线免费观看 | 精品免费久久久久久久 | 69视频在线观看免费 | 麻豆性生活 | 国产在线区 | 免费av网站在线播放 | 成人欧美在线 | av在线播放一区 | 日韩欧美中文字幕在线观看 | 免费看毛片的网站 | xsmax国产精品 | 免费的黄色片 | 国产欧美日韩久久 | 久久久中文字幕日本无吗 | 牛牛精品一区二区 | 少妇性l交大片免费观看冫 少妇性l交大片免费快色 | 国产一区二区亚洲 | 俺来俺也去www色在线观看 | 一本大道加勒比免费视频 | 99re6这里只有精品 | 天天爱天天插 | 欧美性大交 | 中文在线资源新版8 | 99国产精品视频免费观看一公开 | 精品无码一区二区三区水蜜桃 | 国产精品高潮呻吟视频 | 精品美女一区二区 | 三级网站免费观看 | 午夜在线免费视频 | 日本另类视频 | 免费观看交性大片 | 中文字幕亚洲在线 | 少妇性l交大片欧洲热妇乱xxx | 性爱免费在线视频 | 欧美另类第一页 | 免费专区丝袜调教视频 | 欧美精品成人a区在线观看 欧美精品成人久久 | 春潮带欲高h1 | 免费无码一区二区三区a片 亚洲欧美日韩国产成人 | 51精品国产人成在线观看 | 午夜无码一区二区三区在线观看 | 日韩精品卡通动漫网站 | 国产精品免费观看视频 | 午夜三级做爰视频在线看 | 久久亚洲国产 | 91小视频| 成人网在线播放 | 成人18aa黄漫免费观看 | 91精品国产综合久久福利不卡 | 日韩一卡2卡3卡新区乱码来袭 | 拔插拔插海外华人永久免费 | 精品国产免费一区二区三区 | 我爱我色成人网 | 国产网站在线免费观看 | 一本大道卡一卡二卡三乱码全集资源 | 日日操中文字幕 | 91精品一区二区三区四区 | 国产精品久久久久久久久免费看 | 午夜av在线 | 99riav国产精品视频 | 欧美乱妇高清无乱码在线观看 | 一级影片在线观看 | 欧美黑人性xxx | 久久强奷乱码老熟女网站 | 女人14毛片毛片毛片毛片区二 | 欧美午夜一区 | 中国白嫩丰满少妇xxxxx明星 | 亚洲 欧美 日韩 国产综合 在线 | 欧美毛片在线观看 | 女人被狂躁c到高潮喷水电影 | 日本少妇做爰全过程二区 | 极品美女扒开粉嫩小泬图片 | 国产男女爽爽爽 | 国产精品一区二区麻豆 | 特黄少妇60分钟在线观看播放 | 激情超碰在线 | 毛片中文字幕 | 日韩少妇诱惑 | 黑人好猛厉害爽受不了好大撑 | 成人同人动漫免费观看 | 九色福利视频 | 成人麻豆日韩在无码视频 | 欧美日韩性生活视频 | 国产熟妇勾子乱视频 | 黄色一级片av | 美女啪啪网 | 岳双腿间已经湿成一片视频 | 国内精品人妻无码久久久影院蜜桃 | 午夜剧场免费在线观看 | 日韩中文字幕精品视频 | 亚洲色图校园春色 | 一本之道高清狼码 | 久久久久久久久久久小说 | 性歌舞团一区二区三区视频 | 午夜精品久久久久久中宇 | 婷婷日韩 | 成人一级片网站 | 精品久久久久久无码专区 | 国产成人精品视频ⅴa片软件竹菊 | 国产va| 狠狠色噜噜狠狠狠888米奇视频 | 日韩一区二区在线看 | 新婚之夜玷污岳丰满少妇在线观看 | 欧美黄色一级生活片 | 毛片日本| a毛片在线观看 | 九九热在线精品视频 | 国产精品一区二区吃奶在线观看 | 午夜黄色在线 | 中文字幕乱视频 | 黄a在线观看 | 国产精品亚洲成在人线 | 噜噜噜久久亚洲精品国产品小说 | 日韩在线一卡 | 精品香蕉一区二区三区 | 亚洲 日韩 激情 无码 中出 | 麻豆人人妻人人妻人人片av | 国产三级久久久精品麻豆三级 | 成年人看的网站 | 色偷偷中文字幕 | 欧美成人一区二区三区 | 无码国模国产在线观看 | 国产在线精品一区二区夜色 | 日本免费三片在线播放 | 激情国产一区二区三区四区小说 | 在线视频三区 | 日日噜夜夜噜 | 自拍偷拍1 | 成人福利视频一区二区 | 国产精品久久国产三级国不卡顿 | 蜜桃久久精品成人无码av | 精品国产一区二区在线观看 | 一级猛片免费看 | 夜夜草av | 黄色福利站 | 小视频成人 | 精品在线免费视频 | 亚洲国产精品一区二区久久hs | 三级在线国产 | 成av人片在线观看www | 狂野欧美性猛交bbbb | 激情五月婷婷综合 | 日本aaaaa级毛片片 | 天天干网址 | 国产老头和老太xxxxx视频 | 国产黄色91| 成人亚洲网| 99国产伦精品一区二区三区 | 欧美啪啪网站 | 大陆熟妇丰满多毛xxxⅹ | 91蝌蚪少妇偷拍 | 深夜少妇18免费 | 欧美日韩国产免费一区二区三区 | 视色影院 | 美女av一区二区三区 | 成人免费观看男女羞羞视频 | 免费视频爱爱太爽了 | 黄色日韩网站 | 日本成熟老妇乱 | 欧美a在线播放 | 婷婷六月网| babes性欧美69 | 国产成人久久精品77777的功能 | 亚洲人成网站在线播放2019 | 亚洲欧美日韩在线 | 亚洲图片88 | 日本色婷婷 | 成人在线免费看视频 | 青青草青青操 | 亚洲二区在线视频 | 成人午夜av国产传媒 | 99精品国产高清一区二区麻豆 | 国产理论在线观看 | a级高清毛片 | 四虎永久在线精品免费网站 | 亚洲精品国偷拍自产在线麻豆 | 婷婷久久久亚洲欧洲日产国码av | 杏导航aⅴ福利网站 | 国产97在线 | 日韩 | 成人天堂视频在线观看软件 | 波多野结衣在线视频免费观看 | 制服 丝袜 综合 日韩 欧美 | 日av一区 | 亚洲国产精品无码av | 欧美激烈精交gif动态图 | 一级片的网站 | av网站亚洲 | av久久久 | 日韩视频一区 | 国产成人精品久久二区二区 | 亚洲精品理论 | www四虎com| 5个黑人躁我一个视频 | 成人性毛片 | 亚洲男人在线 | 十二月综合缴缴情小说 | 久久艹精品 | 久久人妻天天av | 狠狠色噜噜狠狠狠四色米奇 | 色妇网 | 成在线人免费视频 | 好吊妞无缓冲视频观看 | 亚洲在线视频免费观看 | 熟女毛多熟妇人妻在线视频 | 亚洲毛片a | 91精品一区二区三区四区 | 免费看片免费播放国产 | 亚洲国产精品成人无久久精品 | 咪咪色影院 | a天堂在线视频 | 秦大爷的性生生活1一7 | 欧美性受xxxx黒人xyx性爽 | 96亚洲精品久久久蜜桃 | 性――交――性――乱 | 免费的男女羞羞视频软件 | 人人妻人人藻人人爽欧美一区 | 伊人亚洲| 沉溺于黑人叶爱中文字幕 | 欧洲亚洲色一区二区色99 | 欧美性狂猛xxxxx深喉 | 久久精品视频在线 | 成人免费午夜视频69影院 | 国产最爽乱淫视频免费 | 看片网站在线观看 | 久久久久国色av免费观看性色 | 国产清纯白嫩初高生视频在线观看 | 日本欧美一区二区三区在线播放 | 奇米久久 | 性猛交xxxxx按摩中国 | 蜜桃av久久久亚洲精品 | 久久综合在线 | 自拍偷拍欧美亚洲 | 日韩www.| 精品一区二区三区四区五区六区 | 亚洲色婷婷久久精品av蜜桃 | 日日摸天天爽天天爽视频 | 日本三级免费 | 日韩精选av | 108种啪姿势大全动态图 | 精品96久久久久久中文字幕无 | 美女的奶胸大爽爽大片 | 国产成人高清视频 | 日本少妇丰满大bbb的小乳沟 | 日本二区三区视频 | 性饥渴的农村熟妇 | 亚洲开心网 | ⅹ一art唯美在线观看 | 国产超碰人人模人人爽人人喊 | 久久久久久黄色 | 国产成人精品午夜片在线观看 | 91av国产在线 | 国产精品一v二v在线观看 | 日本特级黄色录像 | 一个色在线 | av资源共享| 成年人黄色大全 | 双性大乳浪受古代h男男 | 国产国产小嫩模无套内谢 | 免费精品视频 | 国产aⅴ精品一区二区三区久久 | 免费啪视频| 亚洲国产毛片aaaaa无费看 | 国产免费又爽又色又粗视频 | 色综合久久久久久久久久 | 精品黑人一区二区三区久久 | 最近中文字幕在线观看视频 | 麻豆出品 | av在线播放器 | 99久久精品国产一区二区蜜芽 | 成年人香蕉视频 | 国产精品污 | 日韩专区一区二区三区 | 天天免费看av | 亚洲人成伊人成综合网久久久 | 697久久夜色精品国产 | 欧美性猛片xxxxx免费中国 | 日韩在线播放一区二区 | 色噜噜狠狠成人中文 | 中文字幕乱码亚洲精品一区 | 久久久久99精品成人片直播 | 国产在线一 | 国内精品国产三级国产在线专 | 欧洲精品一区二区 | 国产精品天干天干在线 | 久久久三区 | eeuss影院在线奇兵区145 | 西西午夜 | 亚洲免费视频网 | 另类专区av | 久久精品亚洲7777影院 | 国产精品一色哟哟哟 | 国模精品视频一区二区 | 黄色一级片免费播放 | 欧美日皮视频 | 亚洲国产一级 | av性色| 奇米四色影视 | 中文一区在线 | 巨人精品福利官方导航 | 欧美性猛交aaaa片黑人 | 熟妇熟女乱妇乱女网站 | 九九爱精品 | 久久久精品欧美一区二区 | 欧美精品一级二级三级 | 国产精品无码人妻一区二区在线 | 娇妻玩4p被三个男人伺候电影 | 四虎影在永久在线观看 | 久久精品波多野结衣 | 久久久久久久国产精品美女 | 99久久精品免费看国产一区二区三区 | 韩国午夜激情 | 国产视频一区二区在线播放 | 中文字幕 亚洲精品 第1页 | 免费asmr色诱娇喘呻吟欧美 | 91文字幕巨乱亚洲香蕉 | 青青青手机在线视频 | 绯色av中文字幕一区三区 | 亚洲精品一区二区三区98年 | 日本69式三人交 | 久久精品视频16 | 清纯小美女主播流白浆 | 一道本道加勒比天天看 | 成熟人妻av无码专区 | 丁香六月久久 | 中文字幕亚洲无线 | 国产欧美精品一区二区三区四区 | 成人av毛片| 三上悠亚人妻中文字幕在线 | 日本美女色视频 | 黑人大战亚洲人精品一区 | 97超碰人人 | 久久精品国产亚卅av嘿嘿 | 松岛枫av在线一区二区 | 乳霸冲田杏梨中文字幕担心学生的 | 一级黄片毛片 | 国产精品久久久久久久久夜色 | 潮喷失禁大喷水aⅴ无码 | 欧美激情免费观看 | 天堂成人在线观看 | 婷婷久久久久久 | 国产裸体永久免费无遮挡 | 日本视频网站在线观看 | av无码精品一区二区三区宅噜噜 | 国产精品国产三级国产a | 国产福利影院 | 黑人vs日本人ⅹxxxhd | 亚洲蜜芽在线精品一区 | 黄色大片黄色大片 | 国产96在线 | 亚洲 | 中国精学生妹品射精久久 | 深夜男女福利18免费软件 | 91狠狠综合 | 人妻夜夜添夜夜无码av | 国产亚洲成av片在线观看 | 国产成人毛片在线视频 | av最新版天堂资源在线 | www国产成人免费观看视频深夜成人网 | 秋霞在线播放视频 | 激情狠狠 | 嫩草影院在线观看视频 | 国产女人精品 | 日本黄色录相 | vr成人啪啪影视 | 国产精品欧美一区二区三区 | 日韩国产一区 | 91豆花精品一区 | 国产欧美综合一区二区三区 | 亚洲熟女一区二区三区 | 日本欧美一区二区三区乱码 | 亚洲天天av | 久久久久久久久久久久 | 超碰色偷偷男人的天堂 | 欧美va亚洲va| 亚洲男人天堂久久 | 精品欧美一区二区精品久久 | 日韩精品中文字幕一区 | 和寡妇做爰过程a一片 | 久久精品国产色蜜蜜麻豆 | 精品国产精品一区二区夜夜嗨 | 日韩在线不卡av | 九九久久在线看 | 九九综合九色综合网站 | 亚洲欧美成人综合 | 久久综合五月丁香久久激情 | 亚洲综合精品一区 | 国产98在线 | 免费、 | 永无久网址在线码观看 | 欧美视频亚洲视频 | 综合伊人 | 国产一区二区观看 | 国产精品亚洲欧美大片在线看 | 一二三四区无产乱码1000集 | 激情五月av | 香蕉视频在线观看www | 国产一区免费看 | 特黄特色大片免费播放 | 国产av激情无码久久 | 6080影视最新97理伦片 | 国产精品久久久久久久久免费看 | 国产三级韩国三级日本带黄 | 亚洲一区在线看 | 日本最新免费二区三区 | 亚洲一区二区三区视频在线 | 欧美激情视频一区 | 极品粉嫩国产18尤物 | 韩国毛片网站 | 国产高潮久久 | 日韩亚洲国产欧美 | 国产一区二区视频播放 | 69av视频在线 | 精品国产系列 | 午夜激情一区二区 | 欧美一级激情 | 91精品一区二区三区在线观看 | 日本中文字幕在线播放 | 91夫妻论坛 | jazzjazz国产精品久久 | 播播成人网 | 熟妇五十路六十路息与子 | 国产麻豆精品一区二区三区v视界 | 好色先生视频污 | 色一情一狱一爱一乱 | 91九色国产ts另类人妖 | 成人三级在线播放 | 国产精品国产三级国产av剧情 | 暖暖视频日本在线观看免费hd | 婷婷去俺也去 | 人人干人人爱 | 欧美一级黄色片网站 | 另类亚洲小说图片综合区 | 一级做a爱高潮免费视频 | 亚洲免费中文字幕 | 亚洲精品日韩综合观看成人91 | 天天操天天操天天 | 亚洲色大成网站www永久在线观看 | 女人内谢aaaa免费视频 | 超级碰碰色偷偷免费视频 | 国产精品免费观看久久 | 翘臀少妇后进一区二区 | 国产亚洲精品久久久久婷婷瑜伽 | 极品福利视频 | 一二三四社区在线中文视频 | 国产清纯白嫩初高生在线观看性色 | 亚洲一区二区黄色 | 神马午夜场 | 亚洲精品网站在线观看 | 超碰97人人做人人爱少妇 | 国产成人精品免费看视频 | 亚洲精品中文字幕乱码 | 欧日韩不卡视频 | 国产伦精品视频一区二区三区 | 欧美性受xxxx黑人xyx性爽 | 日韩精品一线二线三线 | 叶子楣裸乳照无奶罩视频 | 欧美午夜精品久久久久久浪潮 | 88国产精品视频一区二区三区 | 亚洲熟妇av日韩熟妇在线 | 播放灌醉水嫩大学生国内精品 | 性做爰视频免费播放大全 | www福利| 毛片视频免费 | 96久久| 日韩怡红院| 午夜资源站 | 另类异族videosex太狠了 | aa级黄色片 | 天堂网www在线 | 欧美性受ⅹ╳╳╳黑人a性爽 | 亚洲综合少妇 | 东北妇女精品bbwbbw | 欧美在线免费观看 | 女人特黄大aaaaaa大片 | 国产乱码精品一区二区三区五月婷 | 免费在线观看中文字幕 | 久久精品国产久精国产 | 婷婷午夜天 | 久久婷婷网站 | 麻豆视频在线观看免费网站黄 | 依依综合网| 四虎永久在线精品免费播放 | 绿帽刺激高潮对白 | 亚洲麻豆 | 女人被弄到高潮的免费视频 | 狠狠色噜噜 | 99自拍偷拍 | 亚洲精品一区二区三区影院忠贞 | 暖暖av| 国产欧美一区二区三区不卡视频 | 亚洲欧美日韩国产 | 免费视频爱爱太爽了 | 美女视频黄色在线观看 | 国产色区| 亚洲va欧美va国产综合定档 | 精品视频免费看 | 男女曰逼视频 | 色yeye香蕉凹凸视频在线观看 | 欧美激情偷拍 | 中文字幕无码毛片免费看 | 天天做天天爱天天综合网2021 | 女女百合高h喷汁呻吟视频 女女百合国产免费网站 | 国产鲁鲁视频在线观看免费 | 中文字幕一区二区三三 | 国产妇女乱码一区二区三区 | 日韩毛片在线视频 | 一级大黄色片 | 国产九九九九九 | 亚色视频在线观看 | 欧美体内谢she精2性欧美 | 亚洲精品成人无码中文毛片不卡 | 国产主播喷水 | 欧美裸体xxxx极品少妇软件 | 亚洲图片 自拍偷拍 | 91丨porny丨国产丝袜福利 | 日韩av影片在线观看 | 久久精品无码专区免费东京热 | 欧美女人天堂 | 最新日韩精品 | 日韩免费精品 | 国产成人黄色片 | 免费公开在线视频 | 18处破外女出血在线 | 观看成人永久免费视频 | 国产毛片毛片精品天天看软件 | 三级黄色在线播放 | 国产精品成人久久久 | 玩弄少妇肉体到高潮动态图 | 亚欧视频在线观看 | 亚洲日本aⅴ片在线观看香蕉 | 一本色道无码不卡在线观看 | 国产无吗一区二区三区在线欢 | 精品国产乱码久久久久久芒果 | 图书馆的女友动漫在线观看 | 久久久免费高清视频 | 日本在线高清不卡免费播放 | 亚洲成人一级片 | 在线天堂v | 成人性生交大片免费看 | 四虎永久在线精品免费观看网站 | 亚欧洲精品在线 | 色妞www精品免费视频 | 在线免费成人 | 又色又爽又高潮免费视频观看 | 成人美女视频 | 欧美疯狂性受xxxxx另类 | 中国丰满猛少妇xxxx | 欧美成人性色 | 国产在线看一区 | 麻豆av免费在线观看 | 国产激情无码一区二区三区 | 天天躁日日躁狠狠躁喷水 | eeuss一区二区三区 | 九九久久久| 熟妇人妻一区二区三区四区 | 性开放少妇xxxxⅹ视频蜜桃 | a猛片免在新观看 | 91网站在线免费观看 | 大胸少妇午夜三级 | 国产精品高潮露脸在线观看 | 国产日产欧美一区二区 | 久久爱水蜜桃69 | 中文字幕巨乳 | 97精品人妻一区二区三区香蕉 | 77777五月色婷婷丁香视频 | 欧美亚洲综合另类色妞网 | 日本japanese丰满少妇 | 久久精品久久久精品美女 | 久久久妇女国产精品影视 | 精品一区二区久久久久久久网站 | 国产成人综合久久 | 亚洲精品久久久久一区二区三区 | 麻豆av福利av久久av | 免费视频毛片 | 麻豆爱爱视频 | 热re99久久精品国99热 | 亚洲色图首页 | 污站在线观看 | 免费看男女做爰爽爽 | 国产精品岛国久久久久 | 日韩中文字幕亚洲欧美 | 成人羞羞国产免费游戏 | 手机av免费看 | 91大神小宝寻花在线观看 | 国产高清女同学巨大乳在线观看 | 亚洲视频欧洲视频 | 一本色道久久综合亚洲精品 | 成人久久久久 | 久久久久国产精品人妻aⅴ毛片 | 久久99精品久久久久久噜噜 | 六月激情综合网 | 日本三级欧美三级 | 激情九九 | 抖音视频在线观看 | 午夜日韩av | 久久精品一区二区三区不卡牛牛 | 欧美成人家庭影院 | 一级黄在线观看 | 亚洲国产欧美不卡在线观看 |