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

服務器之家:專注于服務器技術及軟件下載分享
分類導航

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

服務器之家 - 編程語言 - Java教程 - java高并發的ReentrantLock重入鎖

java高并發的ReentrantLock重入鎖

2022-03-02 01:00路人甲Java Java教程

這篇文章主要介紹了如何教你完全理解ReentrantLock重入鎖,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,下面我們來一起學習一下吧

synchronized的局限性

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

1.當線程嘗試獲取鎖的時候,如果獲取不到鎖會一直阻塞,這個阻塞的過程,用戶無法控制

2.如果獲取鎖的線程進入休眠或者阻塞,除非當前線程異常,否則其他線程嘗試獲取鎖必須一直等待

JDK1.5之后發布,加入了Doug Lea實現的java.util.concurrent包。包內提供了Lock類,用來提供更多擴展的加鎖功能。Lock彌補了synchronized的局限,提供了更加細粒度的加鎖功能。

 

ReentrantLock

ReentrantLock是Lock的默認實現,在聊ReentranLock之前,我們需要先弄清楚一些概念:

1.可重入鎖:可重入鎖是指同一個線程可以多次獲得同一把鎖;ReentrantLock和關鍵字Synchronized都是可重入鎖

2.可中斷鎖:可中斷鎖時只線程在獲取鎖的過程中,是否可以相應線程中斷操作。synchronized是不可中斷的,ReentrantLock是可中斷的

3.公平鎖和非公平鎖:公平鎖是指多個線程嘗試獲取同一把鎖的時候,獲取鎖的順序按照線程到達的先后順序獲取,而不是隨機插隊的方式獲取。synchronized是非公平鎖,而ReentrantLock是兩種都可以實現,不過默認是非公平鎖

 

ReentrantLock基本使用

我們使用3個線程來對一個共享變量++操作,先使用synchronized實現,然后使用ReentrantLock實現。

synchronized方式:

package com.itsoku.chat06;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo2 {	
  private static int num = 0;	
  private static synchronized void add() {	
      num++;	
  }	
  public static class T extends Thread {	
      @Override	
      public void run() {	
          for (int i = 0; i < 10000; i++) {	
              Demo2.add();	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T();	
      T t2 = new T();	
      T t3 = new T();	
      t1.start();	
      t2.start();	
      t3.start();	
      t1.join();	
      t2.join();	
      t3.join();	
      System.out.println(Demo2.num);	
  }	
}

ReentrantLock方式:

package com.itsoku.chat06;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo3 {	
  private static int num = 0;	
  private static ReentrantLock lock = new ReentrantLock();	
  private static void add() {	
      lock.lock();	
      try {	
          num++;	
      } finally {	
          lock.unlock();	
      }	
  }	
  public static class T extends Thread {	
      @Override	
      public void run() {	
          for (int i = 0; i < 10000; i++) {	
              Demo3.add();	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T();	
      T t2 = new T();	
      T t3 = new T();	
      t1.start();	
      t2.start();	
      t3.start();	
      t1.join();	
      t2.join();	
      t3.join();	
      System.out.println(Demo3.num);	
  }	
}

ReentrantLock的使用過程:

1.創建鎖:ReentrantLock lock = new ReentrantLock();

2.獲取鎖:lock.lock()

3.釋放鎖:lock.unlock();

對比上面的代碼,與關鍵字synchronized相比,ReentrantLock鎖有明顯的操作過程,開發人員必須手動的指定何時加鎖,何時釋放鎖,正是因為這樣手動控制,ReentrantLock對邏輯控制的靈活度要遠遠勝于關鍵字synchronized,上面代碼需要注意lock.unlock()一定要放在finally中,否則,若程序出現了異常,鎖沒有釋放,那么其他線程就再也沒有機會獲取這個鎖了。

 

ReentrantLock是可重入鎖

來驗證一下ReentrantLock是可重入鎖,實例代碼:

package com.itsoku.chat06;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo4 {	
  private static int num = 0;	
  private static ReentrantLock lock = new ReentrantLock();	
  private static void add() {	
      lock.lock();	
      lock.lock();	
      try {	
          num++;	
      } finally {	
          lock.unlock();	
          lock.unlock();	
      }	
  }	
  public static class T extends Thread {	
      @Override	
      public void run() {	
          for (int i = 0; i < 10000; i++) {	
              Demo4.add();	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T();	
      T t2 = new T();	
      T t3 = new T();	
      t1.start();	
      t2.start();	
      t3.start();	
      t1.join();	
      t2.join();	
      t3.join();	
      System.out.println(Demo4.num);	
  }	
}

上面代碼中add()方法中,當一個線程進入的時候,會執行2次獲取鎖的操作,運行程序可以正常結束,并輸出和期望值一樣的30000,假如ReentrantLock是不可重入的鎖,那么同一個線程第2次獲取鎖的時候由于前面的鎖還未釋放而導致死鎖,程序是無法正常結束的。ReentrantLock命名也挺好的Re entrant Lock,和其名字一樣,可重入鎖。

代碼中還有幾點需要注意:

1.lock()方法和unlock()方法需要成對出現,鎖了幾次,也要釋放幾次,否則后面的線程無法獲取鎖了;可以將add中的unlock刪除一個事實,上面代碼運行將無法結束

2.unlock()方法放在finally中執行,保證不管程序是否有異常,鎖必定會釋放

 

ReentrantLock實現公平鎖

在大多數情況下,鎖的申請都是非公平的,也就是說,線程1首先請求鎖A,接著線程2也請求了鎖A。那么當鎖A可用時,是線程1可獲得鎖還是線程2可獲得鎖呢?這是不一定的,系統只是會從這個鎖的等待隊列中隨機挑選一個,因此不能保證其公平性。這就好比買票不排隊,大家都圍在售票窗口前,售票員忙的焦頭爛額,也顧及不上誰先誰后,隨便找個人出票就完事了,最終導致的結果是,有些人可能一直買不到票。而公平鎖,則不是這樣,它會按照到達的先后順序獲得資源。公平鎖的一大特點是:它不會產生饑餓現象,只要你排隊,最終還是可以等到資源的;synchronized關鍵字默認是有jvm內部實現控制的,是非公平鎖。而ReentrantLock運行開發者自己設置鎖的公平性。

看一下jdk中ReentrantLock的源碼,2個構造方法:

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

默認構造方法創建的是非公平鎖。

第2個構造方法,有個fair參數,當fair為true的時候創建的是公平鎖,公平鎖看起來很不錯,不過要實現公平鎖,系統內部肯定需要維護一個有序隊列,因此公平鎖的實現成本比較高,性能相對于非公平鎖來說相對低一些。因此,在默認情況下,鎖是非公平的,如果沒有特別要求,則不建議使用公平鎖。

公平鎖和非公平鎖在程序調度上是很不一樣,來一個公平鎖示例看一下:

package com.itsoku.chat06;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo5 {	
  private static int num = 0;	
  private static ReentrantLock fairLock = new ReentrantLock(true);	
  public static class T extends Thread {	
      public T(String name) {	
          super(name);	
      }	
      @Override	
      public void run() {	
          for (int i = 0; i < 5; i++) {	
              fairLock.lock();	
              try {	
                  System.out.println(this.getName() + "獲得鎖!");	
              } finally {	
                  fairLock.unlock();	
              }	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T("t1");	
      T t2 = new T("t2");	
      T t3 = new T("t3");	
      t1.start();	
      t2.start();	
      t3.start();	
      t1.join();	
      t2.join();	
      t3.join();	
  }	
}

運行結果輸出:

t1獲得鎖!
t2獲得鎖!
t3獲得鎖!
t1獲得鎖!
t2獲得鎖!
t3獲得鎖!
t1獲得鎖!
t2獲得鎖!
t3獲得鎖!
t1獲得鎖!
t2獲得鎖!
t3獲得鎖!
t1獲得鎖!
t2獲得鎖!
t3獲得鎖!

看一下輸出的結果,鎖時按照先后順序獲得的。

修改一下上面代碼,改為非公平鎖試試,如下:

ReentrantLock fairLock = new ReentrantLock(false);

運行結果如下:

t1獲得鎖!	
t3獲得鎖!	
t3獲得鎖!	
t3獲得鎖!	
t3獲得鎖!	
t1獲得鎖!	
t1獲得鎖!	
t1獲得鎖!	
t1獲得鎖!	
t2獲得鎖!	
t2獲得鎖!	
t2獲得鎖!	
t2獲得鎖!	
t2獲得鎖!	
t3獲得鎖!

可以看到t3可能會連續獲得鎖,結果是比較隨機的,不公平的。

 

ReentrantLock獲取鎖的過程是可中斷的

對于synchronized關鍵字,如果一個線程在等待獲取鎖,最終只有2種結果:

1.要么獲取到鎖然后繼續后面的操作

2.要么一直等待,直到其他線程釋放鎖為止

而ReentrantLock提供了另外一種可能,就是在等的獲取鎖的過程中(發起獲取鎖請求到還未獲取到鎖這段時間內)是可以被中斷的,也就是說在等待鎖的過程中,程序可以根據需要取消獲取鎖的請求。有些使用這個操作是非常有必要的。比如:你和好朋友越好一起去打球,如果你等了半小時朋友還沒到,突然你接到一個電話,朋友由于突發狀況,不能來了,那么你一定達到回府。中斷操作正是提供了一套類似的機制,如果一個線程正在等待獲取鎖,那么它依然可以收到一個通知,被告知無需等待,可以停止工作了。

示例代碼:

package com.itsoku.chat06;	
import java.util.concurrent.TimeUnit;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo6 {	
  private static ReentrantLock lock1 = new ReentrantLock(false);	
  private static ReentrantLock lock2 = new ReentrantLock(false);	
  public static class T extends Thread {	
      int lock;	
      public T(String name, int lock) {	
          super(name);	
          this.lock = lock;	
      }	
      @Override	
      public void run() {	
          try {	
              if (this.lock == 1) {	
                  lock1.lockInterruptibly();	
                  TimeUnit.SECONDS.sleep(1);	
                  lock2.lockInterruptibly();	
              } else {	
                  lock2.lockInterruptibly();	
                  TimeUnit.SECONDS.sleep(1);	
                  lock1.lockInterruptibly();	
              }	
          } catch (InterruptedException e) {	
              System.out.println("中斷標志:" + this.isInterrupted());	
              e.printStackTrace();	
          } finally {	
              if (lock1.isHeldByCurrentThread()) {	
                  lock1.unlock();	
              }	
              if (lock2.isHeldByCurrentThread()) {	
                  lock2.unlock();	
              }	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T("t1", 1);	
      T t2 = new T("t2", 2);	
      t1.start();	
      t2.start();	
  }	
}

先運行一下上面代碼,發現程序無法結束,使用jstack查看線程堆棧信息,發現2個線程死鎖了。

Found one Java-level deadlock:	
=============================	
"t2":	
waiting for ownable synchronizer 0x0000000717380c20, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),	
which is held by "t1"	
"t1":	
waiting for ownable synchronizer 0x0000000717380c50, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),	
which is held by "t2"

lock1被線程t1占用,lock2倍線程t2占用,線程t1在等待獲取lock2,線程t2在等待獲取lock1,都在相互等待獲取對方持有的鎖,最終產生了死鎖,如果是在synchronized關鍵字情況下發生了死鎖現象,程序是無法結束的。

我們隊上面代碼改造一下,線程t2一直無法獲取到lock1,那么等待5秒之后,我們中斷獲取鎖的操作。主要修改一下main方法,如下:

T t1 = new T("t1", 1);	
T t2 = new T("t2", 2);	
t1.start();	
t2.start();	
TimeUnit.SECONDS.sleep(5);	
t2.interrupt();

新增了2行代碼TimeUnit.SECONDS.sleep(5);t2.interrupt();,程序可以結束了,運行結果:

java.lang.InterruptedException	
  at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)	
  at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)	
  at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)	
  at com.itsoku.chat06.Demo6$T.run(Demo6.java:31)	
中斷標志:false

從上面信息中可以看出,代碼的31行觸發了異常,中斷標志輸出:false

java高并發的ReentrantLock重入鎖

t2在31行一直獲取不到lock1的鎖,主線程中等待了5秒之后,t2線程調用了interrupt()方法,將線程的中斷標志置為true,此時31行會觸發InterruptedException異常,然后線程t2可以繼續向下執行,釋放了lock2的鎖,然后線程t1可以正常獲取鎖,程序得以繼續進行。線程發送中斷信號觸發InterruptedException異常之后,中斷標志將被清空。

關于獲取鎖的過程中被中斷,注意幾點:

1.ReentrankLock中必須使用實例方法lockInterruptibly()獲取鎖時,在線程調用interrupt()方法之后,才會引發InterruptedException異常

2.線程調用interrupt()之后,線程的中斷標志會被置為true

3.觸發InterruptedException異常之后,線程的中斷標志有會被清空,即置為false

4.所以當線程調用interrupt()引發InterruptedException異常,中斷標志的變化是:false->true->false

ReentrantLock鎖申請等待限時

申請鎖等待限時是什么意思?一般情況下,獲取鎖的時間我們是不知道的,synchronized關鍵字獲取鎖的過程中,只能等待其他線程把鎖釋放之后才能夠有機會獲取到所。所以獲取鎖的時間有長有短。如果獲取鎖的時間能夠設置超時時間,那就非常好了。

ReentrantLock剛好提供了這樣功能,給我們提供了獲取鎖限時等待的方法tryLock(),可以選擇傳入時間參數,表示等待指定的時間,無參則表示立即返回鎖申請的結果:true表示獲取鎖成功,false表示獲取鎖失敗。

tryLock無參方法

看一下源碼中tryLock方法:

public boolean tryLock()

返回boolean類型的值,此方法會立即返回,結果表示獲取鎖是否成功,示例:

package com.itsoku.chat06;	
import java.util.concurrent.TimeUnit;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo8 {	
  private static ReentrantLock lock1 = new ReentrantLock(false);	
  public static class T extends Thread {	
      public T(String name) {	
          super(name);	
      }	
      @Override	
      public void run() {	
          try {	
              System.out.println(System.currentTimeMillis() + ":" + this.getName() + "開始獲取鎖!");	
              //獲取鎖超時時間設置為3秒,3秒內是否能否獲取鎖都會返回	
              if (lock1.tryLock()) {	
                  System.out.println(System.currentTimeMillis() + ":" + this.getName() + "獲取到了鎖!");	
                  //獲取到鎖之后,休眠5秒	
                  TimeUnit.SECONDS.sleep(5);	
              } else {	
                  System.out.println(System.currentTimeMillis() + ":" + this.getName() + "未能獲取到鎖!");	
              }	
          } catch (InterruptedException e) {	
              e.printStackTrace();	
          } finally {	
              if (lock1.isHeldByCurrentThread()) {	
                  lock1.unlock();	
              }	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T("t1");	
      T t2 = new T("t2");	
      t1.start();	
      t2.start();	
  }	
}

代碼中獲取鎖成功之后,休眠5秒,會導致另外一個線程獲取鎖失敗,運行代碼,輸出:

1563356291081:t2開始獲取鎖!
1563356291081:t2獲取到了鎖!
1563356291081:t1開始獲取鎖!
1563356291081:t1未能獲取到鎖!

可以看到t2獲取成功,t1獲取失敗了,tryLock()是立即響應的,中間不會有阻塞。

tryLock有參方法

可以明確設置獲取鎖的超時時間,該方法簽名:

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException

該方法在指定的時間內不管是否可以獲取鎖,都會返回結果,返回true,表示獲取鎖成功,返回false表示獲取失敗。此方法由2個參數,第一個參數是時間類型,是一個枚舉,可以表示時、分、秒、毫秒等待,使用比較方便,第1個參數表示在時間類型上的時間長短。此方法在執行的過程中,如果調用了線程的中斷interrupt()方法,會觸發InterruptedException異常。

示例:

package com.itsoku.chat06;	
import java.util.concurrent.TimeUnit;	
import java.util.concurrent.locks.ReentrantLock;	
/**	
* 微信公眾號:路人甲Java,專注于java技術分享(帶你玩轉 爬蟲、分布式事務、異步消息服務、任務調度、分庫分表、大數據等),喜歡請關注!	
*/	
public class Demo7 {	
  private static ReentrantLock lock1 = new ReentrantLock(false);	
  public static class T extends Thread {	
      public T(String name) {	
          super(name);	
      }	
      @Override	
      public void run() {	
          try {	
              System.out.println(System.currentTimeMillis() + ":" + this.getName() + "開始獲取鎖!");	
              //獲取鎖超時時間設置為3秒,3秒內是否能否獲取鎖都會返回	
              if (lock1.tryLock(3, TimeUnit.SECONDS)) {	
                  System.out.println(System.currentTimeMillis() + ":" + this.getName() + "獲取到了鎖!");	
                  //獲取到鎖之后,休眠5秒	
                  TimeUnit.SECONDS.sleep(5);	
              } else {	
                  System.out.println(System.currentTimeMillis() + ":" + this.getName() + "未能獲取到鎖!");	
              }	
          } catch (InterruptedException e) {	
              e.printStackTrace();	
          } finally {	
              if (lock1.isHeldByCurrentThread()) {	
                  lock1.unlock();	
              }	
          }	
      }	
  }	
  public static void main(String[] args) throws InterruptedException {	
      T t1 = new T("t1");	
      T t2 = new T("t2");	
      t1.start();	
      t2.start();	
  }	
}

程序中調用了ReentrantLock的實例方法tryLock(3,TimeUnit.SECONDS),表示獲取鎖的超時時間是3秒,3秒后不管是否能否獲取鎖,該方法都會有返回值,獲取到鎖之后,內部休眠了5秒,會導致另外一個線程獲取鎖失敗。

運行程序,輸出:

1563355512901:t2開始獲取鎖!
1563355512901:t1開始獲取鎖!
1563355512902:t2獲取到了鎖!
1563355515904:t1未能獲取到鎖!

輸出結果中分析,t2獲取到鎖了,然后休眠了5秒,t1獲取鎖失敗,t1打印了2條信息,時間相差3秒左右。

關于tryLock()方法和tryLock(long timeout, TimeUnit unit)方法,說明一下:

1.都會返回boolean值,結果表示獲取鎖是否成功

2.tryLock()方法,不管是否獲取成功,都會立即返回;而有參的tryLock方法會嘗試在指定的時間內去獲取鎖,中間會阻塞的現象,在指定的時間之后會不管是否能夠獲取鎖都會返回結果

3.tryLock()方法不會響應線程的中斷方法;而有參的tryLock方法會響應線程的中斷方法,而出發InterruptedException異常,這個從2個方法的聲明上可以可以看出來

 

ReentrantLock其他常用的方法

isHeldByCurrentThread:實例方法,判斷當前線程是否持有ReentrantLock的鎖,上面代碼中有使用過。

 

獲取鎖的4種方法對比

獲取鎖的方法 是否立即響應(不會阻塞) 是否響應中斷
lock() × ×
lockInterruptibly() ×
tryLock() ×
tryLock(long timeout, TimeUnit unit) ×

 

總結

1.ReentrantLock可以實現公平鎖和非公平鎖

2.ReentrantLock默認實現的是非公平鎖

3.ReentrantLock的獲取鎖和釋放鎖必須成對出現,鎖了幾次,也要釋放幾次

4.釋放鎖的操作必須放在finally中執行

5.lockInterruptibly()實例方法可以相應線程的中斷方法,調用線程的interrupt()方法時,lockInterruptibly()方法會觸發InterruptedException異常

6.關于InterruptedException異常說一下,看到方法聲明上帶有throwsInterruptedException,表示該方法可以相應線程中斷,調用線程的interrupt()方法時,這些方法會觸發InterruptedException異常,觸發InterruptedException時,線程的中斷中斷狀態會被清除。所以如果程序由于調用interrupt()方法而觸發InterruptedException異常,線程的標志由默認的false變為ture,然后又變為false

7.實例方法tryLock()獲會嘗試獲取鎖,會立即返回,返回值表示是否獲取成功

8.實例方法tryLock(long timeout, TimeUnit unit)會在指定的時間內嘗試獲取鎖,指定的時間內是否能夠獲取鎖,都會返回,返回值表示是否獲取鎖成功,該方法會響應線程的中斷

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://itsoku.blog.csdn.net/article/details/100036282

延伸 · 閱讀

精彩推薦
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
主站蜘蛛池模板: 国产精品中文字幕在线 | 亚洲精品视频播放 | 国产一区二区三区久久 | 国产精品一区久久久 | 久久一二区 | 免费的污网站 | 国产一区二区精品在线观看 | 久久精品 | 久久国产精品系列 | 亚洲品质自拍视频网站 | 日韩在线免费 | 成人羞羞网站 | 青青草中文字幕 | 国产精品久久久久久婷婷天堂 | 中文字幕一区二区三 | 久久精品播放 | 欧美一级二级三级视频 | 在线a免费 | 黄色影视在线免费观看 | 狠狠躁夜夜躁人人爽天天高潮 | 欧美区视频 | 一呦二呦三呦国产精品 | 欧美大片一区 | 国产一区二区三区高清 | 欧美日本国产一区 | 精品无码三级在线观看视频 | 天堂资源最新在线 | 欧洲国产一区 | 国产一区二区视频在线观看 | 国产精品福利一区二区三区 | 欧美精品1区| 日本一级淫片免费看 | 欧美精品成人 | 欧美在线视频一区 | 欧美日韩精品免费 | 日韩一区二区不卡 | 日韩一二三 | 在线观看成人高清 | 黄视频在线观看免费 | 国产成人在线视频 | 欧美日韩精品免费 |