多線程小抄集(新編一)

      網友投稿 778 2025-04-05

      ### Java中線程的狀態

      NEW、RUNNABLE(RUNNING or READY)、BLOCKED、WAITING、TIME_WAITING、TERMINATED

      ![在這里插入圖片描述](https://img-blog.csdnimg.cn/20181229163204752.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyNTY4MTY=,size_16,color_FFFFFF,t_70)

      Java將操作系統中的運行和就緒兩個狀態合并稱為運行狀態。阻塞狀態是線程阻塞在進入synchronized關鍵字修飾的方法或代碼塊(獲取鎖)時的狀態,但是阻塞在JUC包中Lock接口的線程狀態卻是等待狀態,因為JUC中Lock接口對于阻塞的實現是通過LockSupport類中的相關方法實現的。

      ### 線程的優先級

      Java中線程的優先級分為1-10這10個等級,如果小于1或大于10則JDK拋出IllegalArgumentException()的異常,默認優先級是5。在Java中線程的優先級具有繼承性,比如A線程啟動B線程,則B線程的優先級與A是一樣的。注意程序正確性不能依賴線程的優先級高低,因為操作系統可以完全不理會Java線程對于優先級的決定。

      ### 守護線程

      Java中有兩種線程:一種是用戶線程,另一種是守護線程。當進程中不存在非守護線程了,則守護線程自動銷毀。通過setDaemon(true)設置線程為后臺線程。注意thread.setDaemon(true)必須在thread.start()之前設置,否則會報IllegalThreadStateException異常;在Daemon線程中產生的新線程也是Daemon的;在使用ExecutorService等多線程框架時,會把守護線程轉換為用戶線程,并且也會把優先級設置為Thread.NORM_PRIORITY。在構建Daemon線程時,不能依靠finally塊中的內容來確保執行關閉或清理資源的邏輯。

      ### 構造線程

      一個新構造的線程對象是由其parent線程來進行空間分配的,而child線程繼承了parent是否為Daemon、優先級、ThreadGroup、加載資源的contextClassLoader以及可繼承的ThreadLocal(InheritableThreadLocal)、同時還會分配一個唯一的ID來標識這個child線程。

      ### 同步不具備繼承性

      當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。同步不具有繼承性(聲明為synchronized的父類方法A,在子類中重寫之后并不具備synchronized的特性)。

      ### 使用多線程的方式

      - extends Thread

      - implements Runnable

      - 使用Future和Callable

      Executor框架使用Runnable作為基本的任務表示形式。Runnable是一種有很大局限的抽象,雖然run能寫入到日志文件或者將結果放入某個共享的數據結構,但它不能返回一個值或拋出一個受檢查的異常。許多任務實際上都是存在延遲的計算——執行數據庫查詢,從網絡上獲取資源,或者計算某個復雜的功能。對于這些任務,Callable是一種更好的抽象:它認為主入口點(call())將返回一個值,并可能拋出一個異常。Runnable和Callable描述的都是抽象的計算任務。這些任務通常是有范圍的,即都有一個明確的起始點,并且最終會結束。

      ### Thread.yield()方法

      yield()方法的作用是放棄當前的CPU資源,將它讓給其他的任務去占用CPU執行時間。但放棄時間不確定,有可能剛剛放棄,馬上又獲得CPU時間片。這里需要注意的是yield()方法和sleep()方法一樣,線程并不會讓出鎖,和wait()不同,這一點也是為什么sleep()方法被設計在Thread類中而不在Object類中的原因。

      ### Thread.sleep(0)

      在線程中,調用sleep(0)可以釋放CPU時間,讓線程馬上重新回到就緒隊列而非等待隊列,sleep(0)釋放當前線程所剩余的時間片(如果有剩余的話),這樣可以讓操作系統切換其他線程來執行,提升效率。

      The semantics of Thread.yield and Thread.sleep(0) are undefined [JLS17.9]; the JVM is free to implement them as no-ops or treat them as scheduling hints. In particular, they are not required to have the semantics of sleep(0) on Unix systems — put the current thread at the end of the run queue for that priority, yielding to other threads of the same priority — though some JVMs implement yield in this way.

      ### Thread.join()

      如果一個線程A執行了thread.join語句,其含義是:當前線程A等待thread線程終止之后才從thread.join()返回。join與synchronized的區別是:join在內部使用wait()方法進行等待,而synchronized關鍵字使用的是“對象監視器”做為同步。join提供了另外兩種實現方法:join(long millis)和join(long millis, int nanos),至多等待多長時間而退出等待(釋放鎖),退出等待之后還可以繼續運行。內部是通過wait方法來實現的。

      ### wait, notify, notifyAll用法

      只能在同步方法或者同步塊中使用wait()方法。在執行wait()方法后,當前線程釋放鎖(這點與sleep和yield方法不同)。調用了wait函數的線程會一直等待,直到有其它線程調用了同一個對象的notify或者notifyAll方法才能被喚醒,需要注意的是:被喚醒并不代表立刻獲得對象的鎖,要等待執行notify()方法的線程執行完,即退出synchronized代碼塊后,當前線程才會釋放鎖,而呈wait狀態的線程才可以獲取該對象鎖。如果調用wait()方法時沒有持有適當的鎖,則拋出IllegalMonitorStateException,它是RuntimeException的一個子類,因此,不需要try-catch語句進行捕獲異常。notify()方法只會(隨機)喚醒一個正在等待的線程,而notifyAll()方法會喚醒所有正在等待的線程。如果一個對象之前沒有調用wait方法,那么調用notify方法是沒有任何影響的。帶參數的wait(long timeout)或者wait(long timeout, int nanos)方法的功能是等待某一時間內是否有線程對鎖進行喚醒,如果超過這個時間則自動喚醒。

      ### setUncaughtExceptionHandler

      當單線程的程序發生一個未捕獲的異常時我們可以采用try....catch進行異常的捕獲,但是在多線程環境中,線程拋出的異常是不能用try....catch捕獲的,這樣就有可能導致一些問題的出現,比如異常的時候無法回收一些系統資源,或者沒有關閉當前的連接等等。Thread的run方法是不拋出任何檢查型異常的,但是它自身卻可能因為一個異常而被中止,導致這個線程的終結。在Thread ApI中提供了UncaughtExceptionHandler,它能檢測出某個由于未捕獲的異常而終結的情況。

      ```

      thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){});

      ```

      同樣可以為所有的Thread設置一個默認的UncaughtExceptionHandler,通過調用Thread.setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)方法,這是Thread的一個static方法。在線程池中,只有通過execute()提交的任務,才能將它拋出的異常交給UncaughtExceptionHandler,而通過submit()提交的任務,無論是拋出的未檢測異常還是已檢查異常,都將被認為是任務返回狀態的一部分。如果既包含setUncaughtExceptionHandler又包含setDefaultUncaughtExceptionHandler,那么會被setUncaughtExceptionHandler處理,setDefaultUncaughtExceptionHandler則忽略。

      ### 關閉鉤子

      JVM既可以正常關閉也可以強制關閉,或者說非正常關閉。關閉鉤子可以在JVM關閉時執行一些特定的操作,譬如可以用于實現服務或應用程序的清理工作。關閉鉤子可以在以下幾種場景中應用:1. 程序正常退出(這里指一個JVM實例);2.使用System.exit();3.終端使用Ctrl+C觸發的中斷;4. 系統關閉;5. OutOfMemory宕機;6.使用Kill pid命令干掉進程(注:在使用kill -9 pid時,是不會被調用的)。使用方法(Runtime.getRuntime().addShutdownHook(Thread hook))。

      ### 終結器finalize

      終結器finalize:在回收器釋放它們后,調用它們的finalize方法,從而保證一些持久化的資源被釋放。在大多數情況下,通過使用finally代碼塊和顯示的close方法,能夠比使用終結器更好地管理資源。唯一例外情況在于:當需要管理對象,并且該對象持有的資源是通過本地方法獲得的。但是基于一些原因(譬如對象復活),我們要盡量避免編寫或者使用包含終結器的類。

      ### 管道

      在Java中提供了各種各樣的輸入/輸出流Stream,使我們能夠很方便地對數據進行操作,其中管道流(pipeStream)是一種特殊的流,用于在不同線程間直接傳送數據。一個線程發送數據到輸出管道,另一個線程從輸入管道中讀數據,通過使用管道,實現不同線程間的通信,而無須借助類似臨時文件之類的東西。在JDK中使用4個類來使線程間可以進行通信:PipedInputStream, PipedOutputStream, PipedReader, PipedWriter。使用代碼類似inputStream.connect(outputStream)或outputStream.connect(inputStream)使兩個Stream之間產生通信連接。

      ### 幾種進程間的通信方式

      - 管道( pipe ):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。

      - 有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。

      - 信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。

      - 消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中并由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式字節流以及緩沖區大小受限等缺點。

      - 信號 ( sinal ) : 信號是一種比較復雜的通信方式,用于通知接收進程某個事件已經發生。

      - 共享內存( shared memory ) :共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的 IPC 方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號兩,配合使用,來實現進程間的同步和通信。

      - 套接字( socket ) : 套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用于不同及其間的進程通信。

      ### synchronized的類鎖與對象鎖

      類鎖:在方法上加上static synchronized的鎖,或者synchronized(xxx.class)的鎖。如下代碼中的method1和method2:

      對象鎖:參考method4,method5,method6。

      ```

      public class LockStrategy

      {

      public Object object1 = new Object();

      public static synchronized void method1(){}

      public void method2(){

      synchronized(LockStrategy.class){}

      }

      public synchronized void method4(){}

      public void method5()

      {

      synchronized(this){}

      }

      public void method6()

      {

      synchronized(object1){}

      }

      }

      ```

      注意方法method4和method5中的同步塊也是互斥的。

      下面做一道習題來加深一下對對象鎖和類鎖的理解,有一個類這樣定義:

      ```

      public class SynchronizedTest

      多線程小抄集(新編一)

      {

      public synchronized void method1(){}

      public synchronized void method2(){}

      public static synchronized void method3(){}

      public static synchronized void method4(){}

      }

      ```

      那么,有SynchronizedTest的兩個實例a和b,對于一下的幾個選項有哪些能被一個以上的線程同時訪問呢?

      A. a.method1() vs. a.method2()

      B. a.method1() vs. b.method1()

      C. a.method3() vs. b.method4()

      D. a.method3() vs. b.method3()

      E. a.method1() vs. a.method3()

      答案是什么呢?BE。

      ### ReentrantLock

      ReentrantLock提供了tryLock方法,tryLock調用的時候,如果鎖被其他線程持有,那么tryLock會立即返回,返回結果為false;如果鎖沒有被其他線程持有,那么當前調用線程會持有鎖,并且tryLock返回的結果為true。

      ```

      boolean tryLock()

      boolean tryLock(long timeout, TimeUnit unit)

      ```

      可以在構造ReentranLock時使用公平鎖,公平鎖是指多個線程在等待同一個鎖時,必須按照申請鎖的先后順序來一次獲得鎖。synchronized中的鎖時非公平的,默認情況下ReentrantLock也是非公平的,但是可以在構造函數中指定使用公平鎖。

      ```

      ReentrantLock()

      ReentrantLock(boolean fair)

      ```

      對于ReentrantLock來說,還有一個十分實用的特性,它可以同時綁定多個Condition條件,以實現更精細化的同步控制。ReentrantLock使用方式如下:

      ```

      Lock lock = new ReentrantLock();

      lock.lock();

      try{

      }finally{

      lock.unlock();

      }

      ```

      在finally塊中釋放鎖,目的是保證在獲取到鎖之后,最終能夠釋放。不要將獲取鎖的過程寫在try塊中,因為如果在獲取鎖時發生了異常,異常拋出的同時也會導致鎖無故釋放。IllegalMonitorStateException。

      ### 公平鎖和非公平鎖只有兩處不同

      - 非公平鎖在調用 lock 后,首先就會調用 CAS 進行一次搶鎖,如果這個時候恰巧鎖沒有被占用,那么直接就獲取到鎖返回了。

      - 非公平鎖在 CAS 失敗后,和公平鎖一樣都會進入到 tryAcquire 方法,在 tryAcquire 方法中,如果發現鎖這個時候被釋放了(state == 0),非公平鎖會直接 CAS 搶鎖,但是公平鎖會判斷等待隊列是否有線程處于等待狀態,如果有則不去搶鎖,乖乖排到后面。

      公平鎖和非公平鎖就這兩點區別,如果這兩次 CAS 都不成功,那么后面非公平鎖和公平鎖是一樣的,都要進入到阻塞隊列等待喚醒。相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。當然,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處于饑餓狀態。

      ### synchronized

      在Java中,每個對象都有兩個池,鎖(monitor)池和等待池:

      1. 鎖池(同步隊列SynchronizedQueue):假設線程A已經擁有了某個對象(注意:不是類)的鎖,而其它的線程想要調用這個對象的某個synchronized方法(或者synchronized塊),由于這些線程在進入對象的synchronized方法之前必須先獲得該對象的鎖的擁有權,但是該對象的鎖目前正被線程A擁有,所以這些線程就進入了該對象的鎖池中。

      2. 等待池(等待隊列WaitQueue):假設一個線程A調用了某個對象的wait()方法,線程A就會釋放該對象的鎖(因為wait()方法必須出現在synchronized中,這樣自然在執行wait()方法之前線程A就已經擁有了該對象的鎖),同時線程A就進入到了該對象的等待池中。如果另外的一個線程調用了相同對象的notifyAll()方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,準備爭奪鎖的擁有權。如果另外的一個線程調用了相同對象的notify()方法,那么僅僅有一個處于該對象的等待池中的線程(隨機)會進入該對象的鎖池。

      synchronized修飾的同步塊使用monitorenter和monitorexit指令,而同步方法則是依靠方法修飾符上的ACC_SYNCHRONIZED來完成的。無論采用哪種方式,其本質上是對一個對象的監視器(monitor)進行獲取,而這個獲取過程是排他的,也就是同一時刻只能有一個線程獲取到synchronized所保護對象的監視器。任意一個對象都擁有自己的監視器,當這個對象由同步塊或者這個對象的同步方法調用時,執行方法的線程必須先獲取到該對象的監視器才能進入同步塊或者同步方法,而沒有獲取到監視器(執行該方法)的線程將會阻塞在同步塊和同步方法的入口處,進入BLOCKED狀態。

      任意線程對Object(Synchronized)的訪問,首先要獲得Object的監視器。如果獲取失敗,線程進入同步隊列(同步隊列SynchronizedQueue),線程狀態變為BLOCKED。當訪問Object的前驅(獲得了鎖的線程)釋放了鎖,則該釋放操作喚醒阻塞在同步隊列中的線程,使其重新嘗試對監視器的獲取。

      ![在這里插入圖片描述](https://img-blog.csdnimg.cn/20181229164151983.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyNTY4MTY=,size_16,color_FFFFFF,t_70)

      wait方法調用后,線程狀態由Runnable變為WAITING/TIME_WAITING,并將當前線程放置到對象的等待隊列(等待隊列WaitQueue)中。notify()方法是將等待隊列中的一個等待線程從等待隊列中移到同步隊列中,而notifyAll方法是將等待隊列中所有的線程全部移到同步隊列,被移動的線程狀態由WAITING變為BLOCKED。

      ![在這里插入圖片描述](https://img-blog.csdnimg.cn/20181229164207427.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyNTY4MTY=,size_16,color_FFFFFF,t_70)

      在鎖對象的對象頭中有一個threadId字段,當第一個線程訪問鎖時,如果該鎖沒有被其他線程訪問過,即threadId字段為空,那么JVM讓其持有偏向鎖,并將threadId字段的值設置為該線程的ID。當下一次獲取鎖時,會判斷當前線程ID是否與鎖對象的threadId一致。如果一致,那么該線程不會再重復獲取鎖,從而提高了程序的運行效率。如果出現鎖的競爭情況,那么偏向鎖會被撤銷并升級為輕量級鎖。如果資源的競爭非常激烈,會升級為重量級鎖。

      ### Condition

      一個Condition和一個Lock關聯在一起,就像一個條件隊列和一個內置鎖相關聯一樣。要創建一個Condition,可以在相關聯的Lock上調用Lock.newCondition方法。正如Lock比內置加鎖提供了更為豐富的功能,Condition同樣比內置條件隊列提供了更豐富的功能:在每個鎖上可存在多個等待、條件等待可以是可中斷的或者不可中斷的、基于時限的等待,以及公平的或非公平的隊列操作。對于每個Lock,可以有任意數量的Condition對象。Condition對象繼承了相關的Lock對象的公平性,對于公平的鎖,線程會依照FIFO順序從Condition.await中釋放。注意:在Condition對象中,與wait,notify和notifyAll方法對應的分別是await,signal,signalAll。但是Condition對Object進行了擴展,因而它也包含wait和notify方法。一定要確保使用的版本——await和signal。

      Condition接口的定義:

      ```

      public interface Condition{

      void await() throws InterruptedException;

      boolean await(long time, TimeUnit unit) throws InterruptedException;

      long awaitNanos(long nanosTimeout) throws InterruptedException;

      void awaitUniterruptibly();

      boolean awaitUntil(Date deadline) throws InterruptedException;

      void signal();

      void signalAll();

      }

      ```

      AQS 中有一個同步隊列(CLH),用于保存等待獲取鎖的線程的隊列。這里我們引入另一個概念,叫等待隊列(condition queue)。

      ![在這里插入圖片描述](https://img-blog.csdnimg.cn/20181229164225289.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyNTY4MTY=,size_16,color_FFFFFF,t_70)

      基本上,把這張圖看懂,你也就知道 condition 的處理流程了:1. 我們知道一個 ReentrantLock 實例可以通過多次調用 newCondition() 來產生多個 Condition 實例,這里對應 condition1 和 condition2。注意,ConditionObject 只有兩個屬性 firstWaiter 和 lastWaiter;2. 每個 condition 有一個關聯的等待隊列,如線程 1 調用 condition1.await() 方法即可將當前線程 1 包裝成 Node 后加入到等待隊列中,然后阻塞在這里,不繼續往下執行,等待隊列是一個單向鏈表;3. 調用 condition1.signal() 會將condition1 對應的等待隊列的 firstWaiter 移到同步隊列的隊尾,等待獲取鎖,獲取鎖后 await 方法返回,繼續往下執行。

      ### ReentrantLock與synchonized區別

      1. ReentrantLock可以中斷地獲取鎖(void lockInterruptibly() throws InterruptedException)

      2. ReentrantLock可以嘗試非阻塞地獲取鎖(boolean tryLock())

      3. ReentrantLock可以超時獲取鎖。通過tryLock(timeout, unit),可以嘗試獲得鎖,并且指定等待的時間。

      4. ReentrantLock可以實現公平鎖。通過new ReentrantLock(true)實現。

      5. ReentrantLock對象可以同時綁定多個Condition對象,而在synchronized中,鎖對象的的wait(), notify(), notifyAll()方法可以實現一個隱含條件,如果要和多于一個的條件關聯的對象,就不得不額外地添加一個鎖,而ReentrantLock則無需這樣做,只需要多次調用newCondition()方法即可。

      Lock接口中的方法:

      ```

      void lock();

      void lockInterruptibly() throws InterruptedException;

      boolean tryLock();

      boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

      void unlock();

      Condition newCondition();

      ```

      ### 重入鎖的實現原理

      為每個鎖關聯一個請求計數和占有他的線程

      ### synchronized與ReentrantLock之間進行選擇

      ReentrantLock與synchronized相比提供了許多功能:定時的鎖等待,可中斷的鎖等待、公平鎖、非阻塞的獲取鎖等,而且從性能上來說ReentrantLock比synchronized略有勝出(JDK6起),在JDK5中是遠遠勝出,為嘛不放棄synchronized呢?ReentrantLock的危險性要比同步機制高,如果忘記在finally塊中調用unlock,那么雖然代碼表面上能正常運行,但實際上已經埋下了一顆定時炸彈,并很可能傷及其他代碼。僅當內置鎖不能滿足需求時,才可以考慮使用ReentrantLock。

      ### 讀寫鎖ReentrantReadWriteLock

      讀寫鎖表示也有兩個鎖,一個是讀操作相關的鎖,也稱為共享鎖;另一個是寫操作相關的鎖,也叫排它鎖。也就是多個讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥。在沒有Thread進行寫操作時,進行讀取操作的多個Thread都可以獲取讀鎖,而進行寫入操作的Thread只有在獲取寫鎖后才能進行寫入操作。即多個Thread可以同時進行讀取操作,但是同一時刻只允許一個Thread進行寫入操作。(lock.readlock.lock(), lock.readlock.unlock, lock.writelock.lock, lock.writelock.unlock)

      鎖降級是指寫鎖降級成讀鎖。如果當前線程擁有寫鎖,然后將其釋放,最后獲取讀鎖,這種分段完成的過程不能稱之為鎖降級。鎖降級是指把持住(當前擁有的)寫鎖,再獲取到讀鎖,最后釋放(先前擁有的)寫鎖的過程。鎖降級中的讀鎖是否有必要呢?答案是必要。主要是為了保證數據的可見性,如果當前線程不獲取讀鎖而是直接釋放寫鎖,假設此刻另一個線程(T)獲取了寫鎖并修改了數據,那么當前線程無法感知線程T的數據更新。如果當前線程獲取讀鎖,即遵循鎖降級的步驟,則線程T將會被阻塞,直到當前線程使用數據并釋放讀鎖之后,線程T才能獲取寫鎖進行數據更新。

      ### 重排序

      是指編譯器和處理器為了優化程序性能而對指令序列進行重新排序的一種手段。

      ### as-if-serial

      不管怎么重排序,單線程程序的執行結構不能被改變。

      Java 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel圖表怎么使用公式排序
      下一篇:在wps表格中如何按姓氏排序(wps如何設置按姓氏排序)
      相關文章
      亚洲人成网站影音先锋播放| 国产成人A亚洲精V品无码| 亚洲av无码成人黄网站在线观看 | 亚洲国产精品久久久久秋霞影院| 亚洲av福利无码无一区二区| 亚洲日本一区二区三区在线| 亚洲中文字幕无码久久2017| 自拍偷自拍亚洲精品情侣| 亚洲一级片免费看| 国产av无码专区亚洲av果冻传媒| 成人午夜亚洲精品无码网站| 亚洲精品无码久久久久| 亚洲精品乱码久久久久66| 亚洲av之男人的天堂网站| 久久久影院亚洲精品| 久久亚洲AV午夜福利精品一区 | 亚洲国产精品无码中文lv| 国产99在线|亚洲| 亚洲熟伦熟女专区hd高清| 亚洲精华国产精华精华液好用 | 亚洲人成电影网站色www| 亚洲国产精品无码中文lv| 综合偷自拍亚洲乱中文字幕| 亚洲阿v天堂在线2017免费| 亚洲情a成黄在线观看| 国内精品久久久久久久亚洲| 亚洲精品乱码久久久久久| 亚洲αv久久久噜噜噜噜噜| 亚洲午夜在线电影| 亚洲乱码日产精品BD在线观看| 亚洲AV成人影视在线观看| 亚洲国产欧美日韩精品一区二区三区| 久久亚洲精品成人无码| 亚洲真人日本在线| 国产成人亚洲精品青草天美| 亚洲国产综合精品中文第一区| 亚洲精品一区二区三区四区乱码| 亚洲一区二区三区久久| 亚洲精品无码久久久久牙蜜区| 亚洲精品WWW久久久久久| 国产国拍亚洲精品mv在线观看|