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

一文搞懂 CountDownLatch 用法和源碼!

開發 前端
本文是 CountDownLatch 的基本使用和源碼分析,CountDownLatch 就是一個基于 AQS 的計數器,它內部的方法都是圍繞 AQS 框架來談的,除此之外還有其他比如 ReentrantLock、Semaphore 等都是 AQS 的實現,所以要研究并發的話,離不開對 AQS 的探討。

 [[358879]]

CountDownLatch 是多線程控制的一種工具,它被稱為 門閥、 計數器或者 閉鎖。這個工具經常用來用來協調多個線程之間的同步,或者說起到線程之間的通信(而不是用作互斥的作用)。下面我們就來一起認識一下 CountDownLatch

認識 CountDownLatch

CountDownLatch 能夠使一個線程在等待另外一些線程完成各自工作之后,再繼續執行。它相當于是一個計數器,這個計數器的初始值就是線程的數量,每當一個任務完成后,計數器的值就會減一,當計數器的值為 0 時,表示所有的線程都已經任務了,然后在 CountDownLatch 上等待的線程就可以恢復執行接下來的任務。

CountDownLatch 的使用

CountDownLatch 提供了一個構造方法,你必須指定其初始值,還指定了 countDown 方法,這個方法的作用主要用來減小計數器的值,當計數器變為 0 時,在 CountDownLatch 上 await 的線程就會被喚醒,繼續執行其他任務。當然也可以延遲喚醒,給 CountDownLatch 加一個延遲時間就可以實現。

其主要方法如下

CountDownLatch 主要有下面這幾個應用場景

CountDownLatch 應用場景

典型的應用場景就是當一個服務啟動時,同時會加載很多組件和服務,這時候主線程會等待組件和服務的加載。當所有的組件和服務都加載完畢后,主線程和其他線程在一起完成某個任務。

CountDownLatch 還可以實現學生一起比賽跑步的程序,CountDownLatch 初始化為學生數量的線程,鳴槍后,每個學生就是一條線程,來完成各自的任務,當第一個學生跑完全程后,CountDownLatch 就會減一,直到所有的學生完成后,CountDownLatch 會變為 0 ,接下來再一起宣布跑步成績。

順著這個場景,你自己就可以延伸、拓展出來很多其他任務場景。

CountDownLatch 用法

下面我們通過一個簡單的計數器來演示一下 CountDownLatch 的用法

  1. public class TCountDownLatch { 
  2.  
  3.     public static void main(String[] args) { 
  4.         CountDownLatch latch = new CountDownLatch(5); 
  5.         Increment increment = new Increment(latch); 
  6.         Decrement decrement = new Decrement(latch); 
  7.  
  8.         new Thread(increment).start(); 
  9.         new Thread(decrement).start(); 
  10.  
  11.         try { 
  12.             Thread.sleep(6000); 
  13.         } catch (InterruptedException e) { 
  14.             e.printStackTrace(); 
  15.         } 
  16.     } 
  17.  
  18. class Decrement implements Runnable { 
  19.  
  20.     CountDownLatch countDownLatch; 
  21.  
  22.     public Decrement(CountDownLatch countDownLatch){ 
  23.         this.countDownLatch = countDownLatch; 
  24.     } 
  25.  
  26.     @Override 
  27.     public void run() { 
  28.         try { 
  29.  
  30.             for(long i = countDownLatch.getCount();i > 0;i--){ 
  31.                 Thread.sleep(1000); 
  32.                 System.out.println("countdown"); 
  33.                 this.countDownLatch.countDown(); 
  34.             } 
  35.  
  36.         } catch (InterruptedException e) { 
  37.             e.printStackTrace(); 
  38.         } 
  39.     } 
  40.  
  41.  
  42. class Increment implements Runnable { 
  43.  
  44.     CountDownLatch countDownLatch; 
  45.  
  46.     public Increment(CountDownLatch countDownLatch){ 
  47.         this.countDownLatch = countDownLatch; 
  48.     } 
  49.  
  50.     @Override 
  51.     public void run() { 
  52.         try { 
  53.             System.out.println("await"); 
  54.             countDownLatch.await(); 
  55.         } catch (InterruptedException e) { 
  56.             e.printStackTrace(); 
  57.         } 
  58.         System.out.println("Waiter Released"); 
  59.     } 

在 main 方法中我們初始化了一個計數器為 5 的 CountDownLatch,在 Decrement 方法中我們使用 countDown 執行減一操作,然后睡眠一段時間,同時在 Increment 類中進行等待,直到 Decrement 中的線程完成計數減一的操作后,喚醒 Increment 類中的 run 方法,使其繼續執行。

下面我們再來通過學生賽跑這個例子來演示一下 CountDownLatch 的具體用法

  1. public class StudentRunRace { 
  2.  
  3.     CountDownLatch stopLatch = new CountDownLatch(1); 
  4.     CountDownLatch runLatch = new CountDownLatch(10); 
  5.  
  6.     public void waitSignal() throws Exception{ 
  7.         System.out.println("選手" + Thread.currentThread().getName() + "正在等待裁判發布口令"); 
  8.         stopLatch.await(); 
  9.         System.out.println("選手" + Thread.currentThread().getName() + "已接受裁判口令"); 
  10.         Thread.sleep((long) (Math.random() * 10000)); 
  11.         System.out.println("選手" + Thread.currentThread().getName() + "到達終點"); 
  12.         runLatch.countDown(); 
  13.     } 
  14.  
  15.     public void waitStop() throws Exception{ 
  16.         Thread.sleep((long) (Math.random() * 10000)); 
  17.         System.out.println("裁判"+Thread.currentThread().getName()+"即將發布口令"); 
  18.         stopLatch.countDown(); 
  19.         System.out.println("裁判"+Thread.currentThread().getName()+"已發送口令,正在等待所有選手到達終點"); 
  20.         runLatch.await(); 
  21.         System.out.println("所有選手都到達終點"); 
  22.         System.out.println("裁判"+Thread.currentThread().getName()+"匯總成績排名"); 
  23.     } 
  24.  
  25.     public static void main(String[] args) { 
  26.         ExecutorService service = Executors.newCachedThreadPool(); 
  27.         StudentRunRace studentRunRace = new StudentRunRace(); 
  28.         for (int i = 0; i < 10; i++) { 
  29.             Runnable runnable = () -> { 
  30.                 try { 
  31.                     studentRunRace.waitSignal(); 
  32.                 } catch (Exception e) { 
  33.                     e.printStackTrace(); 
  34.                 } 
  35.             }; 
  36.             service.execute(runnable); 
  37.         } 
  38.         try { 
  39.             studentRunRace.waitStop(); 
  40.         } catch (Exception e) { 
  41.             e.printStackTrace(); 
  42.         } 
  43.         service.shutdown(); 
  44.     } 

下面我們就來一起分析一下 CountDownLatch 的源碼

CountDownLatch 源碼分析

CountDownLatch 使用起來比較簡單,但是卻非常有用,現在你可以在你的工具箱中加上 CountDownLatch 這個工具類了。下面我們就來深入認識一下 CountDownLatch。

CountDownLatch 的底層是由 AbstractQueuedSynchronizer 支持,而 AQS 的數據結構的核心就是兩個隊列,一個是 同步隊列(sync queue),一個是條件隊列(condition queue)。

Sync 內部類

CountDownLatch 在其內部是一個 Sync ,它繼承了 AQS 抽象類。

  1. private static final class Sync extends AbstractQueuedSynchronizer {...} 

CountDownLatch 其實其內部只有一個 sync 屬性,并且是 final 的

  1. private final Sync sync; 

CountDownLatch 只有一個帶參數的構造方法

  1. public CountDownLatch(int count) { 
  2.   if (count < 0) throw new IllegalArgumentException("count < 0"); 
  3.   this.sync = new Sync(count); 

也就是說,初始化的時候必須指定計數器的數量,如果數量為負會直接拋出異常。

然后把 count 初始化為 Sync 內部的 count,也就是

  1. Sync(int count) { 
  2.   setState(count); 

注意這里有一個 setState(count),這是什么意思呢?見聞知意這只是一個設置狀態的操作,但是實際上不單單是,還有一層意思是 state 的值代表著待達到條件的線程數。這個我們在聊 countDown 方法的時候再討論。

getCount() 方法的返回值是 getState() 方法,它是 AbstractQueuedSynchronizer 中的方法,這個方法會返回當前線程計數,具有 volatile 讀取的內存語義。

  1. // ---- CountDownLatch ---- 
  2.  
  3. int getCount() { 
  4.   return getState(); 
  5.  
  6. // ---- AbstractQueuedSynchronizer ---- 
  7.  
  8. protected final int getState() { 
  9.   return state; 

tryAcquireShared() 方法用于獲取·共享狀態下對象的狀態,判斷對象是否為 0 ,如果為 0 返回 1 ,表示能夠嘗試獲取,如果不為 0,那么返回 -1,表示無法獲取。

  1. protected int tryAcquireShared(int acquires) { 
  2.   return (getState() == 0) ? 1 : -1; 
  3.  
  4. // ----  getState() 方法和上面的方法相同 ---- 

這個 共享狀態 屬于 AQS 中的概念,在 AQS 中分為兩種模式,一種是 獨占模式,一種是 共享模式。

  • tryAcquire 獨占模式,嘗試獲取資源,成功則返回 true,失敗則返回 false。
  • tryAcquireShared 共享方式,嘗試獲取資源。負數表示失敗;0 表示成功,但沒有剩余可用資源;正數表示成功,且有剩余資源。

tryReleaseShared() 方法用于共享模式下的釋放

  1. protected boolean tryReleaseShared(int releases) { 
  2.   // 減小數量,變為 0 的時候進行通知。 
  3.   for (;;) { 
  4.     int c = getState(); 
  5.     if (c == 0) 
  6.       return false
  7.     int nextc = c-1; 
  8.     if (compareAndSetState(c, nextc)) 
  9.       return nextc == 0; 
  10.   } 

這個方法是一個無限循環,獲取線程狀態,如果線程狀態是 0 則表示沒有被線程占有,沒有占有的話那么直接返回 false ,表示已經釋放;然后下一個狀態進行 - 1 ,使用 compareAndSetState CAS 方法進行和內存值的比較,如果內存值也是 1 的話,就會更新內存值為 0 ,判斷 nextc 是否為 0 ,如果 CAS 比較不成功的話,會再次進行循環判斷。

await 方法

await() 方法是 CountDownLatch 一個非常重要的方法,基本上可以說只有 countDown 和 await 方法才是 CountDownLatch 的精髓所在,這個方法將會使當前線程在 CountDownLatch 計數減至零之前一直等待,除非線程被中斷。

CountDownLatch 中的 await 方法有兩種,一種是不帶任何參數的 await(),一種是可以等待一段時間的await(long timeout, TimeUnit unit)。下面我們先來看一下 await() 方法。

  1. public void await() throws InterruptedException { 
  2.   sync.acquireSharedInterruptibly(1); 

await 方法內部會調用 acquireSharedInterruptibly 方法,這個 acquireSharedInterruptibly 是 AQS 中的方法,以共享模式進行中斷。

  1. public final void acquireSharedInterruptibly(int arg) 
  2.   throws InterruptedException { 
  3.   if (Thread.interrupted()) 
  4.     throw new InterruptedException(); 
  5.   if (tryAcquireShared(arg) < 0) 
  6.     doAcquireSharedInterruptibly(arg); 

可以看到,acquireSharedInterruptibly 方法的內部會首先判斷線程是否中斷,如果線程中斷,則直接拋出線程中斷異常。如果沒有中斷,那么會以共享的方式獲取。如果能夠在共享的方式下不能獲取鎖,那么就會以共享的方式斷開鏈接。

  1. private void doAcquireSharedInterruptibly(int arg) 
  2.   throws InterruptedException { 
  3.   final Node node = addWaiter(Node.SHARED); 
  4.   boolean failed = true
  5.   try { 
  6.     for (;;) { 
  7.       final Node p = node.predecessor(); 
  8.       if (p == head) { 
  9.         int r = tryAcquireShared(arg); 
  10.         if (r >= 0) { 
  11.           setHeadAndPropagate(node, r); 
  12.           p.next = null; // help GC 
  13.           failed = false
  14.           return
  15.         } 
  16.       } 
  17.       if (shouldParkAfterFailedAcquire(p, node) && 
  18.           parkAndCheckInterrupt()) 
  19.         throw new InterruptedException(); 
  20.     } 
  21.   } finally { 
  22.     if (failed) 
  23.       cancelAcquire(node); 
  24.   } 

這個方法有些長,我們分開來看

  • 首先,會先構造一個共享模式的 Node 入隊
  • 然后使用無限循環判斷新構造 node 的前驅節點,如果 node 節點的前驅節點是頭節點,那么就會判斷線程的狀態,這里調用了一個 setHeadAndPropagate ,其源碼如下
  1. private void setHeadAndPropagate(Node node, int propagate) { 
  2.   Node h = head;  
  3.   setHead(node); 
  4.   if (propagate > 0 || h == null || h.waitStatus < 0 || 
  5.       (h = head) == null || h.waitStatus < 0) { 
  6.     Node s = node.next
  7.     if (s == null || s.isShared()) 
  8.       doReleaseShared(); 
  9.   } 

首先會設置頭節點,然后進行一系列的判斷,獲取節點的獲取節點的后繼,以共享模式進行釋放,就會調用 doReleaseShared 方法,我們再來看一下 doReleaseShared 方法

  1. private void doReleaseShared() { 
  2.  
  3.   for (;;) { 
  4.     Node h = head; 
  5.     if (h != null && h != tail) { 
  6.       int ws = h.waitStatus; 
  7.       if (ws == Node.SIGNAL) { 
  8.         if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
  9.           continue;            // loop to recheck cases 
  10.         unparkSuccessor(h); 
  11.       } 
  12.       else if (ws == 0 && 
  13.                !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
  14.         continue;                // loop on failed CAS 
  15.     } 
  16.     if (h == head)                   // loop if head changed 
  17.       break; 
  18.   } 

這個方法會以無限循環的方式首先判斷頭節點是否等于尾節點,如果頭節點等于尾節點的話,就會直接退出。如果頭節點不等于尾節點,會判斷狀態是否為 SIGNAL,不是的話就繼續循環 compareAndSetWaitStatus,然后斷開后繼節點。如果狀態不是 SIGNAL,也會調用 compareAndSetWaitStatus 設置狀態為 PROPAGATE,狀態為 0 并且不成功,就會繼續循環。

也就是說 setHeadAndPropagate 就是設置頭節點并且釋放后繼節點的一系列過程。

  • 我們來看下面的 if 判斷,也就是 shouldParkAfterFailedAcquire(p, node) 這里
  1. if (shouldParkAfterFailedAcquire(p, node) && 
  2.     parkAndCheckInterrupt()) 
  3.   throw new InterruptedException(); 

如果上面 Node p = node.predecessor() 獲取前驅節點不是頭節點,就會進行 park 斷開操作,判斷此時是否能夠斷開,判斷的標準如下

  1. private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { 
  2.   int ws = pred.waitStatus; 
  3.   if (ws == Node.SIGNAL) 
  4.     return true
  5.   if (ws > 0) { 
  6.     do { 
  7.       node.prev = pred = pred.prev; 
  8.     } while (pred.waitStatus > 0); 
  9.     pred.next = node; 
  10.   } else { 
  11.     compareAndSetWaitStatus(pred, ws, Node.SIGNAL); 
  12.   } 
  13.   return false

這個方法會判斷 Node p 的前驅節點的結點狀態(waitStatus),節點狀態一共有五種,分別是

  1. CANCELLED(1):表示當前結點已取消調度。當超時或被中斷(響應中斷的情況下),會觸發變更為此狀態,進入該狀態后的結點將不會再變化。
  2. SIGNAL(-1):表示后繼結點在等待當前結點喚醒。后繼結點入隊時,會將前繼結點的狀態更新為 SIGNAL。
  3. CONDITION(-2):表示結點等待在 Condition 上,當其他線程調用了 Condition 的 signal() 方法后,CONDITION狀態的結點將從等待隊列轉移到同步隊列中,等待獲取同步鎖。
  4. PROPAGATE(-3):共享模式下,前繼結點不僅會喚醒其后繼結點,同時也可能會喚醒后繼的后繼結點。
  5. 0:新結點入隊時的默認狀態。

如果前驅節點是 SIGNAL 就會返回 true 表示可以斷開,如果前驅節點的狀態大于 0 (此時為什么不用 ws == Node.CANCELLED ) 呢?因為 ws 大于 0 的條件只有 CANCELLED 狀態了。然后就是一系列的查找遍歷操作直到前驅節點的 waitStatus > 0。如果 ws <= 0 ,而且還不是 SIGNAL 狀態的話,就會使用 CAS 替換前驅節點的 ws 為 SIGNAL 狀態。

如果檢查判斷是中斷狀態的話,就會返回 false。

  1. private final boolean parkAndCheckInterrupt() { 
  2.   LockSupport.park(this); 
  3.   return Thread.interrupted(); 

這個方法使用 LockSupport.park 斷開連接,然后返回線程是否中斷的標志。

  • cancelAcquire() 用于取消等待隊列,如果等待過程中沒有成功獲取資源(如timeout,或者可中斷的情況下被中斷了),那么取消結點在隊列中的等待。
  1. private void cancelAcquire(Node node) { 
  2.   if (node == null
  3.     return
  4.  
  5.   node.thread = null
  6.  
  7.   Node pred = node.prev; 
  8.   while (pred.waitStatus > 0) 
  9.     node.prev = pred = pred.prev; 
  10.  
  11.   Node predNext = pred.next
  12.  
  13.   node.waitStatus = Node.CANCELLED; 
  14.  
  15.   if (node == tail && compareAndSetTail(node, pred)) { 
  16.     compareAndSetNext(pred, predNext, null); 
  17.   } else { 
  18.     int ws; 
  19.     if (pred != head && 
  20.         ((ws = pred.waitStatus) == Node.SIGNAL || 
  21.          (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && 
  22.         pred.thread != null) { 
  23.       Node next = node.next
  24.       if (next != null && next.waitStatus <= 0) 
  25.         compareAndSetNext(pred, predNext, next); 
  26.     } else { 
  27.       unparkSuccessor(node); 
  28.     } 
  29.     node.next = node; // help GC 
  30.   } 

所以,對 CountDownLatch 的 await 調用大致會有如下的調用過程。

一個和 await 重載的方法是 await(long timeout, TimeUnit unit),這個方法和 await 最主要的區別就是這個方法能夠可以等待計數器一段時間再執行后續操作。

countDown 方法

countDown 是和 await 同等重要的方法,countDown 用于減少計數器的數量,如果計數減為 0 的話,就會釋放所有的線程。

  1. public void countDown() { 
  2.   sync.releaseShared(1); 

這個方法會調用 releaseShared 方法,此方法用于共享模式下的釋放操作,首先會判斷是否能夠進行釋放,判斷的方法就是 CountDownLatch 內部類 Sync 的 tryReleaseShared 方法

  1. public final boolean releaseShared(int arg) { 
  2.   if (tryReleaseShared(arg)) { 
  3.     doReleaseShared(); 
  4.     return true
  5.   } 
  6.   return false
  7.  
  8. // ---- CountDownLatch ---- 
  9.  
  10. protected boolean tryReleaseShared(int releases) { 
  11.   for (;;) { 
  12.     int c = getState(); 
  13.     if (c == 0) 
  14.       return false
  15.     int nextc = c-1; 
  16.     if (compareAndSetState(c, nextc)) 
  17.       return nextc == 0; 
  18.   } 

tryReleaseShared 會進行 for 循環判斷線程狀態值,使用 CAS 不斷嘗試進行替換。

如果能夠釋放,就會調用 doReleaseShared 方法

  1. private void doReleaseShared() { 
  2.   for (;;) { 
  3.     Node h = head; 
  4.     if (h != null && h != tail) { 
  5.       int ws = h.waitStatus; 
  6.       if (ws == Node.SIGNAL) { 
  7.         if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
  8.           continue;            // loop to recheck cases 
  9.         unparkSuccessor(h); 
  10.       } 
  11.       else if (ws == 0 && 
  12.                !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
  13.         continue;                // loop on failed CAS 
  14.     } 
  15.     if (h == head)                   // loop if head changed 
  16.       break; 
  17.   } 

可以看到,doReleaseShared 其實也是一個無限循環不斷使用 CAS 嘗試替換的操作。

總結

本文是 CountDownLatch 的基本使用和源碼分析,CountDownLatch 就是一個基于 AQS 的計數器,它內部的方法都是圍繞 AQS 框架來談的,除此之外還有其他比如 ReentrantLock、Semaphore 等都是 AQS 的實現,所以要研究并發的話,離不開對 AQS 的探討。CountDownLatch 的源碼看起來很少,比較簡單,但是其內部比如 await 方法的調用鏈路卻很長,也值得花費時間深入研究。

本文轉載自微信公眾號「 Java建設者」,可以通過以下二維碼關注。轉載本文請聯系 Java建設者公眾號。

 

責任編輯:武曉燕 來源: Java建設者
相關推薦

2023-10-16 08:16:31

Bean接口類型

2021-12-29 17:38:17

JavaScripttypeof前端

2024-04-12 12:19:08

語言模型AI

2022-03-24 08:51:48

Redis互聯網NoSQL

2019-11-06 17:30:57

cookiesessionWeb

2021-03-22 10:05:59

netstat命令Linux

2023-09-08 08:20:46

ThreadLoca多線程工具

2023-09-15 12:00:01

API應用程序接口

2023-02-10 10:56:56

KubernetesLimitsRequests

2024-09-27 08:10:57

2020-11-04 07:49:04

Select

2023-08-24 16:50:45

2024-06-05 11:43:10

2023-04-03 15:04:00

RPCPHP語言

2019-11-19 08:00:00

神經網絡AI人工智能

2020-03-18 14:00:47

MySQL分區數據庫

2022-06-07 10:13:22

前端沙箱對象

2021-06-30 08:45:02

內存管理面試

2022-08-15 15:39:23

JavaScript面向對象數據

2021-01-13 05:21:59

參數
點贊
收藏

51CTO技術棧公眾號

国产精品精品国产一区二区| 亚洲成人1区| 久久综合色之久久综合| 国产精品福利久久久| 丝袜美腿小色网| 亚洲裸色大胆大尺寸艺术写真| 欧美日韩你懂的| 又大又硬又爽免费视频| 91精品专区| 成人va在线观看| 国产精品自产拍在线观| 日韩av一区二区在线播放| 精品久久久久久久久久久下田| 欧美精品自拍偷拍| 日本在线观看a| 粗大黑人巨茎大战欧美成人| 久久久久久久电影| 国产精品区二区三区日本| 中文字幕一区二区三区四区欧美| 一区二区在线| 永久555www成人免费| 麻豆精品国产传媒av| 日韩成人一区| 欧美性感美女h网站在线观看免费| 天堂v在线视频| 国产一级网站视频在线| 成人晚上爱看视频| 成人福利视频在线观看| 在线视频一区二区三区四区| 欧美精品99| 在线日韩中文字幕| 巨胸大乳www视频免费观看| 亚洲国产aⅴ精品一区二区| 欧美性受xxxx黑人xyx性爽| 黄页网站在线观看视频| 在线中文字幕视频观看| 中文字幕中文字幕在线一区| 欧美日韩在线观看一区二区三区| 成人精品在线播放| 国产精品亚洲专一区二区三区 | 成人动漫一区二区| 亚洲在线免费视频| 国产精品无码白浆高潮| 蜜桃久久精品一区二区| 国产suv精品一区二区| 日韩欧美a级片| 99国产精品久久久久久久成人热| 欧美丰满少妇xxxx| 黄色一级片中国| 国产精品伦理久久久久久| 中文字幕日韩欧美| 中文字幕在线观看二区| 日韩片欧美片| 日韩一级黄色av| 国产成人av免费在线观看| 日韩伦理一区| 久久久精品国产网站| 天天鲁一鲁摸一摸爽一爽| 97视频精品| 欧美wwwxxxx| 五月婷婷一区二区| 一区视频在线看| 97视频在线免费观看| 中文字幕国产在线观看| 天堂成人国产精品一区| 国产精品一区二区三区免费视频 | 国产精品蜜月aⅴ在线| 欧美综合一区二区| 在线观看免费的av| 嫩呦国产一区二区三区av| 欧美成人女星排名| 在线观看国产网站| 精品国产一区二区三区av片| 最新国产成人av网站网址麻豆| 五月综合色婷婷| 亚洲视频高清| 国产999视频| 国产又粗又猛视频| 风间由美性色一区二区三区| 精品一区二区三区免费毛片| 日本一卡二卡四卡精品| 中文字幕高清一区| 日韩欧美一级在线| 小早川怜子影音先锋在线观看| 在线视频你懂得一区二区三区| jizz大全欧美jizzcom| 日本精品国产| 亚洲精品天天看| 好吊日在线视频| 亚洲专区一区| 成人激情视频网| 天堂在线免费av| 中文字幕亚洲一区二区va在线| 国产免费一区二区视频| 78精品国产综合久久香蕉| 欧美一区二区三区免费观看视频| 三级黄色片网站| 天天影视欧美综合在线观看| 911国产网站尤物在线观看| 中文字幕第2页| 成人高清在线视频| 亚洲国产婷婷香蕉久久久久久99| 欧美家庭影院| 欧美三级电影在线看| 在线看黄色的网站| 亚洲va在线| 国产精品久在线观看| 亚洲爱情岛论坛永久| 中文一区在线播放| 国产二级片在线观看| 国产精品白丝久久av网站| 国产婷婷成人久久av免费高清 | 在线观看的av| 精品成人av一区| 久久久精品视频国产| 欧美日韩伦理在线免费| 国产最新精品视频| a在线观看视频| 国产精品久线观看视频| 99爱视频在线| 盗摄牛牛av影视一区二区| 久久精品国产综合| 瑟瑟视频在线免费观看| 91免费视频网址| 日本精品久久久久久久久久| 一区二区三区| 中文字幕无线精品亚洲乱码一区| 久久青青草视频| 成人18精品视频| 九一免费在线观看| 精品一区二区三区四区五区| 在线视频日韩精品| 欧美黄色一级大片| 91小视频免费观看| 国产成人在线免费看| 国产成人一二片| 精品中文字幕在线2019| 国产免费黄色大片| 中文字幕在线不卡国产视频| 国产小视频精品| 欧美日韩一二三四| 国产精品免费久久久久久| 免费国产在线观看| 色婷婷久久99综合精品jk白丝| 手机在线看片日韩| 一区二区三区高清视频在线观看| 国产精品夜夜夜一区二区三区尤| 金瓶狂野欧美性猛交xxxx| 日韩精品综合一本久道在线视频| 色哟哟一一国产精品| 韩国av一区二区三区四区| 中文字幕av导航| 亚洲国产中文在线| 欧美国产日韩一区| 狠狠躁日日躁夜夜躁av| 午夜影视日本亚洲欧洲精品| 亚洲中文字幕一区| 欧美一级专区| 日本一区二区三区视频在线观看 | 精品免费国产| 一区二区三区四区日本视频| 亚洲人成亚洲人成在线观看| 久久精品久久久久久久| 国产精品色一区二区三区| 午夜av中文字幕| 国产一区激情| 欧美一区二区三区精美影视| 国产成人久久精品麻豆二区| 久久国产精品久久久| 免费av网站在线播放| 欧美日韩亚洲国产一区| 影音先锋男人在线| 国产美女精品在线| www.99热这里只有精品| 日韩欧美中文字幕电影| 国产精品午夜国产小视频| 成人影欧美片| 亚洲精品国产精品久久清纯直播| 91青青草视频| 亚洲女爱视频在线| 日本黄色录像片| 老司机精品视频在线| 大胆欧美熟妇xx| 国际精品欧美精品| 92看片淫黄大片欧美看国产片| caoporn-草棚在线视频最| 国产亚洲福利一区| 亚洲AV无码国产精品午夜字幕| 精品国产鲁一鲁一区二区张丽| 影音先锋男人看片资源| 丁香激情综合五月| 日本在线观看免费视频| 伊人久久婷婷| 亚洲欧洲另类精品久久综合| 国内毛片久久| 91精品国产综合久久男男 | 中国xxxx性xxxx产国| 日韩成人伦理电影在线观看| www.在线观看av| 色97色成人| 欧美极品视频一区二区三区| 欧美日韩中出| 国产精品久久久久9999| 91九色在线播放| 精品国产一区二区三区久久久| 亚洲av片一区二区三区| 日韩免费性生活视频播放| 亚洲无码精品一区二区三区| 亚洲电影激情视频网站| www.5588.com毛片| 国产女人aaa级久久久级| 中文字幕在线观看91| 精品一区二区三区不卡| 国产极品美女高潮无套久久久| 欧美日本中文| 熟妇熟女乱妇乱女网站| 波多野结衣的一区二区三区| 国产在线欧美日韩| 日韩一区二区三区色| 国产热re99久久6国产精品| 黄色亚洲网站| 97香蕉久久超级碰碰高清版| 男女免费观看在线爽爽爽视频| 日韩网站免费观看| 91精彩视频在线播放| 亚洲欧美日韩直播| 天堂av在线资源| 亚洲级视频在线观看免费1级| 999国产精品视频免费| 欧美日韩午夜在线视频| 免费av中文字幕| 欧美在线视频全部完| 手机av免费观看| 色94色欧美sute亚洲线路一ni| 精品成人av一区二区在线播放| 亚洲福利视频导航| 国产精品theporn动漫| 一区二区三区四区在线播放| 四虎精品免费视频| 亚洲欧洲日韩综合一区二区| 波多野结衣家庭教师在线观看| 国产清纯美女被跳蛋高潮一区二区久久w | 国产成人亚洲综合色影视| xxxx在线免费观看| 精彩视频一区二区三区| 欧美一级视频在线| 久久国产精品露脸对白| 看看黄色一级片| 国产伦精一区二区三区| 粗大的内捧猛烈进出视频| 国产成人免费在线视频| 亚洲欧洲国产视频| av电影天堂一区二区在线观看| 国产高潮失禁喷水爽到抽搐| 99在线精品免费| 亚洲综合网在线观看| 国产午夜久久久久| 亚洲AV成人无码网站天堂久久| 国产精品久久夜| 国产探花在线免费观看| 亚洲综合色视频| 亚州国产精品视频| 一本到不卡免费一区二区| 日韩不卡高清视频| 欧美二区在线观看| 午夜美女福利视频| 亚洲欧美日韩中文在线| 在线免费观看的av网站| 欧美日韩成人精品| 天堂中文最新版在线中文| 国产精品成久久久久三级| 91精品网站在线观看| av噜噜色噜噜久久| 亚洲专区视频| 天天综合五月天| 国产欧美一级| av污在线观看| 成人的网站免费观看| 男人舔女人下部高潮全视频| 自拍偷拍国产亚洲| 色网站在线播放| 欧美日韩免费视频| 蜜臀久久99精品久久久| 一区二区三区久久精品| 影音先锋中文在线视频| 欧洲成人在线观看| 精品中文字幕一区二区三区| 久精品国产欧美| 亚洲国产精品久久久天堂| 国产妇女馒头高清泬20p多| 青青青爽久久午夜综合久久午夜| xxxx视频在线观看| 欧美激情在线一区二区三区| 国产一级二级三级视频| 在线亚洲一区二区| 日本韩国在线观看| 精品久久久91| 成人福利视频| 国产富婆一区二区三区| 精品久久久亚洲| www.av蜜桃| 国产中文字幕精品| 免费观看a级片| 亚洲成人一区在线| 国产精品自产拍| 国产亚洲精品激情久久| 超碰在线网站| 91九色视频导航| 啪啪亚洲精品| 日本福利视频在线| 国产suv精品一区二区6| 日韩欧美视频免费观看| 日韩欧美成人精品| 欧美自拍偷拍一区二区| 日韩最新在线视频| 成人国产精品一区二区免费麻豆| 国产综合动作在线观看| 欧美一区综合| 精品国产乱码久久久久久1区二区| 国产日韩欧美综合一区| 圆产精品久久久久久久久久久| 精品捆绑美女sm三区| 老司机精品视频在线观看6| 国产精品大陆在线观看| 亚洲欧洲美洲国产香蕉| 国产综合av在线| 99久久综合99久久综合网站| 久久精品www| 日韩三级中文字幕| 欧美女同一区| yellow视频在线观看一区二区| 女生裸体视频一区二区三区| 亚洲性图一区二区| 国产精品免费视频网站| 中文字幕激情视频| 亚洲午夜未删减在线观看| 日本欧美日韩| 日本一区二区三区四区高清视频 | 久久久久久久久久久妇女| 污片在线免费看| 国产精品嫩草影院com| 国产精品露脸视频| 一区二区欧美激情| 国产精品久久久久久久久免费高清 | 激情亚洲成人| 800av在线播放| 欧美日韩一区二区精品| 欧美日韩国产中文字幕在线| 欧美在线激情视频| 亚洲综合福利| 亚洲一区二区三区四区五区xx| 国产精品欧美经典| 国产精品久久久久久免费免熟 | 极品魔鬼身材女神啪啪精品| 91.麻豆视频| 久久香蕉av| 久久久久资源| 日本不卡视频在线观看| 黄色免费一级视频| 911精品产国品一二三产区| 手机av免费在线| 国产乱码精品一区二区三区不卡| 国产精品日韩久久久| 一级片视频免费看| 欧美精选午夜久久久乱码6080| 天堂亚洲精品| 狼狼综合久久久久综合网| 日韩av一区二区在线影视| 老湿机69福利| 亚洲国产高清福利视频| 欧美中文字幕精在线不卡| 一区二区在线观| 成人综合婷婷国产精品久久免费| 日韩欧美一级视频| 中文字幕日韩欧美精品在线观看| 日韩欧美中文字幕一区二区三区| 国产免费黄色小视频| 国产精品美女视频| 亚洲国产日韩在线观看| 日产日韩在线亚洲欧美| 99热精品久久| 色天使在线视频| 7777精品伊人久久久大香线蕉经典版下载| 国产丝袜在线观看视频| 日韩av电影免费观看| 国产乱码精品一区二区三区av| 800av免费在线观看| 精品国产一区二区在线| 电影中文字幕一区二区| av天堂永久资源网| 亚洲人成伊人成综合网小说| 青青色在线视频| 2022国产精品| 蜜臀国产一区二区三区在线播放| 久久久久99精品成人片毛片| 在线观看国产成人av片| 久久综合五月婷婷| 日韩欧美色视频| 欧美自拍丝袜亚洲| 僵尸再翻生在线观看免费国语|