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

從Java走進Scala:深入了解Scala并發性

開發 后端
對于許多(如果不是大多數)Java 程序員來說,Scala 的吸引力在于處理并發性以及編寫線程安全的代碼時非常輕松。在本期文章中,Ted Neward 將開始深入研究 Scala 語言及環境所提供的各種并發特性和庫。

2003 年,Herb Sutter 在他的文章 “The Free Lunch Is Over” 中揭露了行業中最不可告人的一個小秘密,他明確論證了處理器在速度上的發展已經走到了盡頭,并且將由全新的單芯片上的并行 “內核”(虛擬 CPU)所取代。這一發現對編程社區造成了不小的沖擊,因為正確創建線程安全的代碼,在理論而非實踐中,始終會提高高性能開發人員的身價,而讓各公司難以聘用他們。看上去,僅有少數人充分理解了 Java 的線程模型、并發 API 以及 “同步” 的含義,以便能夠編寫同時提供安全性和吞吐量的代碼 —— 并且大多數人已經明白了它的困難所在。

51CTO編輯推薦:Scala編程語言專題

據推測,行業的其余部分將自力更生,這顯然不是一個理想的結局,至少不是 IT 部門努力開發軟件所應得的回報。

與 Scala 在 .NET 領域中的姐妹語言 F# 相似,Scala 是針對 “并發性問題” 的解決方案之一。在本期文章中,我討論了 Scala 的一些屬性,這些屬性使它更加勝任于編寫線程安全的代碼,比如默認不可修改的對象,并討論了一種返回對象副本而不是修改它們內容的***設計方案。Scala 對并發性的支持遠比此深遠;現在,我們有必要來了解一下 Scala 的各種庫。

并發性基礎

在深入研究 Scala 的并發性支持之前,有必要確保您具備了對 Java 基本并發性模型的良好理解,因為 Scala 的并發性支持,從某種程度上說,建立在 JVM 和支持庫所提供的特性和功能的基礎之上。為此,清單 1 中的代碼包含了一個已知的 Producer/Consumer 并發性問題(詳見 Sun Java Tutorial 的 “Guarded Blocks” 小節)。注意,Java Tutorial 版本并未在其解決方案中使用 java.util.concurrent 類,而是擇優使用了 java.lang.Object 中的較舊的 wait()/notifyAll() 方法:

清單 1. Producer/Consumer(Java5 之前)

  1. package com.tedneward.scalaexamples.notj5;  
  2.  
  3. class Producer implements Runnable  
  4. {  
  5.   private Drop drop;  
  6.   private String importantInfo[] = {  
  7.     "Mares eat oats",  
  8.     "Does eat oats",  
  9.     "Little lambs eat ivy",  
  10.     "A kid will eat ivy too" 
  11.   };  
  12.  
  13.   public Producer(Drop drop) { this.drop = drop; }  
  14.  
  15.   public void run()  
  16.   {  
  17.     for (int i = 0; i < importantInfo.length; i++)  
  18.     {  
  19.       drop.put(importantInfo[i]);  
  20.     }  
  21.     drop.put("DONE");  
  22.   }  
  23. }  
  24.  
  25. class Consumer implements Runnable  
  26. {  
  27.   private Drop drop;  
  28.  
  29.   public Consumer(Drop drop) { this.drop = drop; }  
  30.  
  31.   public void run()  
  32.   {  
  33.     for (String message = drop.take(); !message.equals("DONE");  
  34.          message = drop.take())  
  35.     {  
  36.       System.out.format("MESSAGE RECEIVED: %s%n", message);  
  37.     }  
  38.   }  
  39. }  
  40.  
  41. class Drop  
  42. {  
  43.   //Message sent from producer to consumer.  
  44.   private String message;  
  45.     
  46.   //True if consumer should wait for producer to send message,  
  47.   //false if producer should wait for consumer to retrieve message.  
  48.   private boolean empty = true;  
  49.  
  50.   //Object to use to synchronize against so as to not "leak" the  
  51.   //"this" monitor  
  52.   private Object lock = new Object();  
  53.  
  54.   public String take()  
  55.   {  
  56.     synchronized(lock)  
  57.     {  
  58.       //Wait until message is available.  
  59.       while (empty)  
  60.       {  
  61.         try 
  62.         {  
  63.           lock.wait();  
  64.         }  
  65.         catch (InterruptedException e) {}  
  66.       }  
  67.       //Toggle status.  
  68.       empty = true;  
  69.       //Notify producer that status has changed.  
  70.       lock.notifyAll();  
  71.       return message;  
  72.     }  
  73.   }  
  74.  
  75.   public void put(String message)  
  76.   {  
  77.     synchronized(lock)  
  78.     {  
  79.       //Wait until message has been retrieved.  
  80.       while (!empty)  
  81.       {  
  82.         try 
  83.         {   
  84.           lock.wait();  
  85.         } catch (InterruptedException e) {}  
  86.       }  
  87.       //Toggle status.  
  88.       empty = false;  
  89.       //Store message.  
  90.       this.message = message;  
  91.       //Notify consumer that status has changed.  
  92.       lock.notifyAll();  
  93.     }  
  94.   }  
  95. }  
  96.  
  97. public class ProdConSample  
  98. {  
  99.   public static void main(String[] args)  
  100.   {  
  101.     Drop drop = new Drop();  
  102.     (new Thread(new Producer(drop))).start();  
  103.     (new Thread(new Consumer(drop))).start();  
  104.   }  
  105. }  

Java 教程 “缺陷”

好奇的讀者可能會將此處的代碼與 Java Tutorial 中的代碼進行比較,尋找它們之間有哪些不同;他們會發現我并未 “同步” put 和 take 方法,而是使用了存儲在 Drop 中的 lock 對象。其原因非常簡單:對象的監測程序永遠都不會封裝在類的內部,因此 Java Tutorial 版本允許此代碼打破此規則(顯然很瘋狂):

  1. public class ProdConSample  
  2. {  
  3.   public static void main(String[] args)  
  4.   {  
  5.     Drop drop = new Drop();  
  6.     (new Thread(new Producer(drop))).start();  
  7.     (new Thread(new Consumer(drop))).start();  
  8.  synchronized(drop)  
  9.  {  
  10.    Thread.sleep(60 * 60 * 24 * 365 * 10); // sleep for 10 years?!?  
  11.  }  
  12.   }  

通過使用私有對象作為鎖定所依托的監測程序,此代碼將不會有任何效果。從本質上說,現在已經封裝了線程安全的實現;然后,它才能依賴客戶機的優勢正常運行。

注意:我在此處展示的代碼對 Sun 教程解決方案做了少許修改;它們提供的代碼存在一個很小的設計缺陷(參見 Java 教程 “缺陷”)。

Producer/Consumer 問題的核心非常容易理解:一個(或多個)生產者實體希望將數據提供給一個(或多個)使用者實體供它們使用和操作(在本例中,它包括將數據打印到控制臺)。Producer 和 Consumer 類是相應直觀的 Runnable-實現類:Producer 從數組中獲取 String,并通過 put 將它們放置到 Consumer 的緩沖區中,并根據需要執行 take。

問題的難點在于,如果 Producer 運行過快,則數據在覆蓋時可能會丟失;如果 Consumer 運行過快,則當 Consumer 讀取相同的數據兩次時,數據可能會得到重復處理。緩沖區(在 Java Tutorial 代碼中稱作 Drop)將確保不會出現這兩種情況。數據破壞的可能性就更不用提了(在 String 引用的例子中很困難,但仍然值得注意),因為數據會由 put 放入緩沖區,并由 take 取出。

關于此主題的全面討論請閱讀 Brian Goetz 的 Java Concurrency in Practice 或 Doug Lea 的 Concurrent Programming in Java(參見 參考資料),但是,在應用 Scala 之前有必要快速了解一下此代碼的運行原理。

當 Java 編譯器看到 synchronized 關鍵字時,它會在同步塊的位置生成一個 try/finally 塊,其頂部包括一個 monitorenter 操作碼,并且 finally 塊中包括一個 monitorexit 操作碼,以確保監控程序(Java 的原子性基礎)已經發布,而與代碼退出的方式無關。因此,Drop 中的 put 代碼將被重寫,如清單 2 所示:

清單 2. 編譯器失效后的 Drop.put 

  1.  // This is pseudocode  
  2.  public void put(String message)  
  3.  {  
  4.    try 
  5.    {  
  6.   monitorenter(lock)  
  7.  
  8.      //Wait until message has been retrieved.  
  9.      while (!empty)  
  10.      {  
  11.        try 
  12.        {   
  13.          lock.wait();  
  14.        } catch (InterruptedException e) {}  
  15.      }  
  16.      //Toggle status.  
  17.      empty = false;  
  18.      //Store message.  
  19.      this.message = message;  
  20.      //Notify consumer that status has changed.  
  21.      lock.notifyAll();  
  22.    }  
  23. finally 
  24. {  
  25.   monitorexit(lock)  
  26. }  
  27.  } 

wait() 方法將通知當前線程進入非活動狀態,并等待另一個線對該對象調用 notifyAll()。然后,通知的線程必須在能夠繼續執行的時候嘗試再次獲取監控程序。從本質上說,wait() 和 notify()/notifyAll() 允許一種簡單的信令機制,它允許 Drop 在 Producer 和 Consumer 線程之間進行協調,每個 put 都有相應的 take。

本文的 代碼下載 部分使用 Java5 并發性增強(Lock 和 Condition 接口以及 ReentrantLock 鎖定實現)提供 清單 2 的基于超時的版本,但基本代碼模式仍然相同。這就是問題所在:編寫清單 2 這樣的代碼的開發人員需要過度專注于線程和鎖定的細節以及低級實現代碼,以便讓它們能夠正確運行。此外,開發人員需要對每一行代碼刨根知底,以確定是否需要保護它們,因為過度同步與過少同步同樣有害。

現在,我們來看到 Scala 替代方案。

#p#

良好的 Scala 并發性 (v1)

開始應用 Scala 并發性的一種方法是將 Java 代碼直接轉換為 Scala,以便利用 Scala 的語法優勢來簡化代碼(至少能簡化一點):

清單 3. ProdConSample (Scala)

  1. object ProdConSample  
  2. {  
  3.   class Producer(drop : Drop)  
  4.     extends Runnable  
  5.   {  
  6.     val importantInfo : Array[String] = Array(  
  7.       "Mares eat oats",  
  8.       "Does eat oats",  
  9.       "Little lambs eat ivy",  
  10.       "A kid will eat ivy too" 
  11.     );  
  12.     
  13.     override def run() : Unit =  
  14.     {  
  15.       importantInfo.foreach((msg) => drop.put(msg))  
  16.       drop.put("DONE")  
  17.     }  
  18.   }  
  19.     
  20.   class Consumer(drop : Drop)  
  21.     extends Runnable  
  22.   {  
  23.     override def run() : Unit =  
  24.     {  
  25.       var message = drop.take()  
  26.       while (message != "DONE")  
  27.       {  
  28.         System.out.format("MESSAGE RECEIVED: %s%n", message)  
  29.         message = drop.take()  
  30.       }  
  31.     }  
  32.   }  
  33.     
  34.   class Drop  
  35.   {  
  36.     var message : String = "" 
  37.     var empty : Boolean = true 
  38.     var lock : AnyRef = new Object()  
  39.     
  40.     def put(x: String) : Unit =  
  41.       lock.synchronized 
  42.       {  
  43.         // Wait until message has been retrieved  
  44.         await (empty == true)  
  45.         // Toggle status  
  46.         empty = false 
  47.         // Store message  
  48.         message = x  
  49.         // Notify consumer that status has changed  
  50.         lock.notifyAll()  
  51.       }  
  52.  
  53.     def take() : String =  
  54.       lock.synchronized 
  55.       {  
  56.         // Wait until message is available.  
  57.         await (empty == false)  
  58.         // Toggle status  
  59.         empty=true 
  60.         // Notify producer that staus has changed  
  61.         lock.notifyAll()  
  62.         // Return the message  
  63.         message  
  64.       }  
  65.  
  66.     private def await(cond: => Boolean) =  
  67.       while (!cond) { lock.wait() }  
  68.   }  
  69.  
  70.   def main(args : Array[String]) : Unit =  
  71.   {  
  72.     // Create Drop  
  73.     val drop = new Drop();  
  74.     
  75.     // Spawn Producer  
  76.     new Thread(new Producer(drop)).start();  
  77.       
  78.     // Spawn Consumer  
  79.     new Thread(new Consumer(drop)).start();  
  80.   }  
  81. }  

Producer 和 Consumer 類幾乎與它們的 Java 同類相同,再一次擴展(實現)了 Runnable 接口并覆蓋了 run() 方法,并且 — 對于 Producer 的情況 — 分別使用了內置迭代方法來遍歷 importantInfo 數組的內容。(實際上,為了讓它更像 Scala,importantInfo 可能應該是一個 List 而不是 Array,但在***次嘗試時,我希望盡可能保證它們與原始 Java 代碼一致。)

Drop 類同樣類似于它的 Java 版本。但 Scala 中有一些例外,“synchronized” 并不是關鍵字,它是針對 AnyRef 類定義的一個方法,即 Scala “所有引用類型的根”。這意味著,要同步某個特定的對象,您只需要對該對象調用同步方法;在本例中,對 Drop 上的 lock 字段中所保存的對象調用同步方法。

注意,我們在 await() 方法定義的 Drop 類中還利用了一種 Scala 機制:cond 參數是等待計算的代碼塊,而不是在傳遞給該方法之前進行計算。在 Scala 中,這被稱作 “call-by-name”;此處,它是一種實用的方法,可以捕獲需要在 Java 版本中表示兩次的條件等待邏輯(分別用于 put 和 take)。

***,在 main() 中,創建 Drop 實例,實例化兩個線程,使用 start() 啟動它們,然后在 main() 的結束部分退出,相信 JVM 會在 main() 結束之前啟動這兩個線程。(在生產代碼中,可能無法保證這種情況,但對于這樣的簡單的例子,99.99% 沒有問題。)

但是,已經說過,仍然存在相同的基本問題:程序員仍然需要過分擔心兩個線程之間的通信和協調問題。雖然一些 Scala 機制可以簡化語法,但這目前為止并沒有相當大的吸引力。

Scala 并發性 v2

Scala Library Reference 中有一個有趣的包:scala.concurrency。這個包包含許多不同的并發性結構,包括我們即將利用的 MailBox 類。

顧名思義,MailBox 從本質上說就是 Drop,用于在檢測之前保存數據塊的單槽緩沖區。但是,MailBox ***的優勢在于它將發送和接收數據的細節完全封裝到模式匹配和 case 類中,這使它比簡單的 Drop(或 Drop 的多槽數據保存類 java.util.concurrent.BoundedBuffer)更加靈活。

清單 4. ProdConSample, v2 (Scala)

  1. package com.tedneward.scalaexamples.scala.V2  
  2. {  
  3.   import concurrent.{MailBox, ops}  
  4.  
  5.   object ProdConSample  
  6.   {  
  7.     class Producer(drop : Drop)  
  8.       extends Runnable  
  9.     {  
  10.       val importantInfo : Array[String] = Array(  
  11.         "Mares eat oats",  
  12.         "Does eat oats",  
  13.         "Little lambs eat ivy",  
  14.         "A kid will eat ivy too" 
  15.       );  
  16.       
  17.       override def run() : Unit =  
  18.       {  
  19.         importantInfo.foreach((msg) => drop.put(msg))  
  20.         drop.put("DONE")  
  21.       }  
  22.     }  
  23.       
  24.     class Consumer(drop : Drop)  
  25.       extends Runnable  
  26.     {  
  27.       override def run() : Unit =  
  28.       {  
  29.         var message = drop.take()  
  30.         while (message != "DONE")  
  31.         {  
  32.           System.out.format("MESSAGE RECEIVED: %s%n", message)  
  33.           message = drop.take()  
  34.         }  
  35.       }  
  36.     }  
  37.  
  38.     class Drop  
  39.     {  
  40.       private val m = new MailBox()  
  41.         
  42.       private case class Empty()  
  43.       private case class Full(x : String)  
  44.         
  45.       m send Empty()  // initialization  
  46.         
  47.       def put(msg : String) : Unit =  
  48.       {  
  49.         m receive  
  50.         {  
  51.           case Empty() =>  
  52.             m send Full(msg)  
  53.         }  
  54.       }  
  55.         
  56.       def take() : String =  
  57.       {  
  58.         m receive  
  59.         {  
  60.           case Full(msg) =>  
  61.             m send Empty(); msg  
  62.         }  
  63.       }  
  64.     }  
  65.     
  66.     def main(args : Array[String]) : Unit =  
  67.     {  
  68.       // Create Drop  
  69.       val drop = new Drop()  
  70.         
  71.       // Spawn Producer  
  72.       new Thread(new Producer(drop)).start();  
  73.         
  74.       // Spawn Consumer  
  75.       new Thread(new Consumer(drop)).start();  
  76.     }  
  77.   }  
  78. }  

此處,v2 和 v1 之間的惟一區別在于 Drop 的實現,它現在利用 MailBox 類處理傳入以及從 Drop 中刪除的消息的阻塞和信號事務。(我們可以重寫 Producer 和 Consumer,讓它們直接使用 MailBox,但考慮到簡單性,我們假定希望保持所有示例中的 Drop API 相一致。)使用 MailBox 與使用典型的 BoundedBuffer(Drop)稍有不同,因此我們來仔細看看其代碼。

MailBox 有兩個基本操作:send 和 receive。receiveWithin 方法僅僅是基于超時的 receive。MailBox 接收任何類型的消息。send() 方法將消息放置到郵箱中,并立即通知任何關心該類型消息的等待接收者,并將它附加到一個消息鏈表中以便稍后檢索。receive() 方法將阻塞,直到接收到對于功能塊合適的消息。

因此,在這種情況下,我們將創建兩個 case 類,一個不包含任何內容(Empty),這表示 MailBox 為空,另一個包含消息數據(Full。

put 方法,由于它會將數據放置在 Drop 中,對 MailBox 調用 receive() 以查找 Empty 實例,因此會阻塞直到發送 Empty。此時,它發送一個 Full 實例給包含新數據的 MailBox。

take 方法,由于它會從 Drop 中刪除數據,對 MailBox 調用 receive() 以查找 Full 實例,提取消息(再次得益于模式匹配從 case 類內部提取值并將它們綁到本地變量的能力)并發送一個 Empty 實例給 MailBox。

不需要明確的鎖定,并且不需要考慮監控程序。

#p#

Scala 并發性 v3

事實上,我們可以顯著縮短代碼,只要 Producer 和 Consumer 不需要功能全面的類(此處便是如此) — 兩者從本質上說都是 Runnable.run() 方法的瘦包裝器,Scala 可以使用 scala.concurrent.ops 對象的 spawn 方法來實現,如清單 5 所示:

清單 5. ProdConSample, v3 (Scala)

  1. package com.tedneward.scalaexamples.scala.V3  
  2. {  
  3.   import concurrent.MailBox  
  4.   import concurrent.ops._  
  5.  
  6.   object ProdConSample  
  7.   {  
  8.     class Drop  
  9.     {  
  10.       private val m = new MailBox()  
  11.         
  12.       private case class Empty()  
  13.       private case class Full(x : String)  
  14.         
  15.       m send Empty()  // initialization  
  16.         
  17.       def put(msg : String) : Unit =  
  18.       {  
  19.         m receive  
  20.         {  
  21.           case Empty() =>  
  22.             m send Full(msg)  
  23.         }  
  24.       }  
  25.         
  26.       def take() : String =  
  27.       {  
  28.         m receive  
  29.         {  
  30.           case Full(msg) =>  
  31.             m send Empty(); msg  
  32.         }  
  33.       }  
  34.     }  
  35.     
  36.     def main(args : Array[String]) : Unit =  
  37.     {  
  38.       // Create Drop  
  39.       val drop = new Drop()  
  40.         
  41.       // Spawn Producer  
  42.       spawn  
  43.       {  
  44.         val importantInfo : Array[String] = Array(  
  45.           "Mares eat oats",  
  46.           "Does eat oats",  
  47.           "Little lambs eat ivy",  
  48.           "A kid will eat ivy too" 
  49.         );  
  50.           
  51.         importantInfo.foreach((msg) => drop.put(msg))  
  52.         drop.put("DONE")  
  53.       }  
  54.         
  55.       // Spawn Consumer  
  56.       spawn  
  57.       {  
  58.         var message = drop.take()  
  59.         while (message != "DONE")  
  60.         {  
  61.           System.out.format("MESSAGE RECEIVED: %s%n", message)  
  62.           message = drop.take()  
  63.         }  
  64.       }  
  65.     }  
  66.   }  
  67. }  

spawn 方法(通過包塊頂部的 ops 對象導入)接收一個代碼塊(另一個 by-name 參數示例)并將它包裝在匿名構造的線程對象的 run() 方法內部。事實上,并不難理解 spawn 的定義在 ops 類的內部是什么樣的:

清單 6. scala.concurrent.ops.spawn()

  1. def spawn(p: => Unit) = {  
  2.   val t = new Thread() { override def run() = p }  
  3.   t.start()  

……這再一次強調了 by-name 參數的強大之處。

ops.spawn 方法的一個缺點在于,它是在 2003 年 Java 5 concurrency 類還不可用的時候編寫的。特別是,java.util.concurrent.Executor 及其同類的作用是讓開發人員更加輕松地生成線程,而不需要實際處理直接創建線程對象的細節。幸運的是,在您自己的自定義庫中重新創建 spawn 的定義是相當簡單的,這需要利用 Executor(或 ExecutorService 或 ScheduledExecutorService)來執行線程的實際啟動任務。

事實上,Scala 的并發性支持超越了 MailBox 和 ops 類;Scala 還支持一個類似的 “Actors” 概念,它使用了與 MailBox 所采用的方法相類似的消息傳遞方法,但應用更加全面并且靈活性也更好。但是,這部分內容將在下期討論。

結束語

Scala 為并發性提供了兩種級別的支持,這與其他與 Java 相關的主題極為類似:

首先,對底層庫的完全訪問(比如說 java.util.concurrent)以及對 “傳統” Java 并發性語義的支持(比如說監控程序和 wait()/notifyAll())。

其次,這些基本機制上面有一個抽象層,詳見本文所討論的 MailBox 類以及將在本系列下一篇文章中討論的 Actors 庫。

兩個例子中的目標是相同的:讓開發人員能夠更加輕松地專注于問題的實質,而不用考慮并發編程的低級細節(顯然,第二種方法更好地實現了這一目標,至少對于沒有過多考慮低級細節的人來說是這樣的。)

但是,當前 Scala 庫的一個明顯的缺陷就是缺乏 Java 5 支持;scala.concurrent.ops 類應該具有 spawn 這樣的利用新的 Executor 接口的方法。它還應該支持利用新的 Lock 接口的各種版本的 synchronized。幸運的是,這些都是可以在 Scala 生命周期中實現的庫增強,而不會破壞已有代碼;它們甚至可以由 Scala 開發人員自己完成,而不需要等待 Scala 的核心開發團隊提供給他們(只需要花費少量時間)。

【相關閱讀】

  1. Scala編程語言專題
  2. 從Java走進Scala:構建計算器 結合解析器組合子和case類
  3. 從Java走進Scala:構建計算器 解析器組合子入門
  4. 從Java走進Scala:簡單的計算器 case類和模式匹配
  5. 從Java走進Scala:包和訪問修飾符
責任編輯:yangsai 來源: IBMDW
相關推薦

2009-09-28 11:01:39

從Java走進Scal

2009-08-21 16:17:25

ScalaTwitter API

2014-05-20 16:27:35

JVMScala

2009-06-17 11:44:22

Scala控制結構

2009-12-09 09:15:47

從Java走進ScalTwitter API

2009-02-04 17:32:03

ibmdwJavaScala

2009-06-16 17:54:38

Scala類語法語義

2009-10-14 11:14:38

ScitterScalaTwitter

2009-08-14 11:35:01

Scala Actor

2009-06-17 13:57:25

Scala元組數組

2009-06-16 17:09:17

Scala面向對象函數編程

2009-06-19 10:51:39

Scalapackage訪問修飾符

2009-06-17 13:26:06

scala繼承模型

2009-06-19 11:13:47

Scalacase類模式匹配

2009-06-19 11:42:09

Scala計算器解析

2010-10-14 13:50:11

Scala

2010-06-23 20:31:54

2010-11-19 16:22:14

Oracle事務

2020-09-21 09:53:04

FlexCSS開發

2022-08-26 13:48:40

EPUBLinux
點贊
收藏

51CTO技術棧公眾號

国产天堂在线播放视频| 中文字幕欧美色图| 欧美人妖在线观看| 色婷婷av一区二区三区软件 | 成人爽a毛片免费啪啪红桃视频| 性做久久久久久免费观看欧美| 欧美在线一二三区| jizz中国女人| 久久一区激情| 久久99久国产精品黄毛片入口 | 中文字幕精品三区| 18成人免费观看网站下载| 日本系列第一页| 久久综合成人| 日韩精品在线免费观看视频| 久久久久xxxx| 欧洲一级精品| 激情av一区二区| 亚洲美女自拍偷拍| 国产专区在线| 成人aaaa免费全部观看| 91九色国产社区在线观看| 亚洲熟女综合色一区二区三区| 永久亚洲成a人片777777| 国产午夜精品理论片a级探花| 欧美在线a视频| 97人人做人人爽香蕉精品| 亚洲成在人线在线播放| 91九色国产ts另类人妖| 爱久久·www| 久久影院视频免费| 国产一级精品aaaaa看| 国产特级黄色片| 免费av成人在线| 日本一区二区在线免费播放| 久久免费黄色网址| 99久久这里只有精品| 国产一区二区三区丝袜| 亚洲av无码一区二区三区网址| 91麻豆精品一二三区在线| 91久久香蕉国产日韩欧美9色| 91成人在线观看喷潮教学| 黄色美女视频在线观看| 一区二区三区四区蜜桃| 欧美日韩一级在线| 免费大片在线观看www| 国产视频视频一区| 欧美一区二区视频在线| 理论在线观看| 久久久久久**毛片大全| 欧美日韩高清免费| 欧美成人免费| 国产亚洲短视频| 日韩精品另类天天更新| 国产福利免费在线观看| 国产亚洲欧美日韩在线一区| 日本免费一区二区三区| 搞黄视频在线观看| 国产精品欧美久久久久一区二区 | 自拍视频一区二区| 日韩激情毛片| 亚洲人成在线电影| 国产不卡在线观看视频| 日韩毛片视频| 久热99视频在线观看| 少妇久久久久久被弄高潮| 欧美日韩ab| 91精品国产亚洲| 波多野结衣小视频| 青青草精品视频| 91精品国产自产在线老师啪| 国产免费无遮挡| 高清不卡一二三区| 久久精品久久精品国产大片| 久青草国产在线| 亚洲欧洲一区二区三区| 日韩一级片一区二区| а√天堂资源官网在线资源| 欧美色图在线视频| 污视频免费在线观看网站| 国产精品久久久久久久久久辛辛| 精品国一区二区三区| 日韩片在线观看| 欧美三级情趣内衣| 理论片在线不卡免费观看| 日韩乱码在线观看| 日本不卡高清视频| 亚洲最大成人在线| 天堂a√中文在线| 综合自拍亚洲综合图不卡区| 国产aaa免费视频| 欧美日韩免费观看视频| 欧美一区二区高清| 欧美黑人欧美精品刺激| 91tv官网精品成人亚洲| 欧美诱惑福利视频| 国产美女www爽爽爽视频| 99久久99久久免费精品蜜臀| 亚洲人成网站在线观看播放| 9999精品成人免费毛片在线看| 日本韩国精品一区二区在线观看| 999久久久精品视频| 秋霞蜜臀av久久电影网免费| 色吧影院999| 国产一级做a爱片久久毛片a| 国产一区二区三区av电影| 久久久久久亚洲精品不卡4k岛国| 黄网站免费在线观看| 日韩欧美aaa| 激情小说欧美色图| 色综合久久网| 欧美亚洲另类激情另类| 亚洲国产精品suv| 中文字幕不卡在线播放| 热99这里只有精品| 国产一区二区久久久久| 国产一区二区三区精品久久久| 国产精品成人久久| 狠狠色狠狠色综合| 先锋在线资源一区二区三区| 不卡专区在线| 日韩欧美的一区| 黄色精品视频在线观看| 首页国产欧美久久| 韩国一区二区三区美女美女秀 | 午夜精品在线视频一区| 制服丝袜中文字幕第一页| 久久av影视| 午夜精品久久久久久久99热| 成人av手机在线| 亚洲欧美自拍偷拍色图| 国产又黄又猛又粗| 国产一区二区三区网| 3344国产精品免费看| 日韩在线视频观看免费| 亚洲狠狠丁香婷婷综合久久久| 国内外成人免费在线视频| 精品一区电影| 国产精品福利片| 电影av一区| 在线观看免费视频综合| 国产精品高清无码在线观看| 天堂在线一区二区| 欧美日韩国产精品一卡| 中文字幕在线免费观看视频| 亚洲免费视频网站| 成人免费毛片男人用品| 久久午夜电影网| 欧美xxxxx在线视频| 欧美女王vk| 国产精品久久久久久影视| 国产裸舞福利在线视频合集| 日本韩国欧美国产| 国产又粗又猛又爽又黄的视频小说| 日韩精品乱码av一区二区| 日韩福利一区二区三区| 成人做爰免费视频免费看| 有码中文亚洲精品| 中文在线字幕免费观| 国产精品色哟哟| 国产精品自在自线| 国产真实久久| 国产三级精品在线不卡| 秋霞伦理一区| 正在播放欧美视频| 亚洲中文一区二区三区| 亚洲男人都懂的| 国产一卡二卡三卡四卡| 亚洲欧美卡通另类91av| 欧美午夜视频在线| 色噜噜成人av在线| 欧美乱妇40p| 色婷婷综合视频| 91福利在线播放| 色哟哟一一国产精品| 国产电影精品久久禁18| 久久久999视频| 精品国产一区一区二区三亚瑟| 国产女人18毛片水18精品| caoporn免费在线| 亚洲高清久久久久久| 亚洲免费视频二区| 一区二区三区中文在线| 亚洲中文字幕无码av| 免费成人在线观看| 国产尤物av一区二区三区| 首页亚洲中字| 成人在线中文字幕| 日韩伦理福利| 麻豆国产精品va在线观看不卡 | 欧美国产日韩亚洲一区| 免费看的av网站| 午夜综合激情| 只有这里有精品| 亚洲美女15p| 亚洲一区二区三区久久| 成人性生交大片免费观看网站| 日韩中文字幕在线播放| 三级在线视频| 日韩无一区二区| 日韩美一区二区| 一区二区三区产品免费精品久久75| 久久久久久九九九九九| 国产成人精品亚洲日本在线桃色 | 99久久精品免费看| 一级黄色片国产| 久久久精品性| 亚洲精品无码国产| 999国产精品永久免费视频app| 久久av一区二区三区漫画| 国产精品成人3p一区二区三区| 日韩美女免费线视频| 久草在线视频网站| 日韩三级成人av网| 川上优的av在线一区二区| 亚洲第一中文字幕在线观看| 国产又粗又猛又黄又爽| 色婷婷av久久久久久久| 日本少妇激情视频| 一区二区三区在线观看欧美| 久久免费手机视频| 国产日韩欧美不卡在线| 亚洲国产精品成人综合久久久| 国产福利精品一区| 午夜一级免费视频| 麻豆91在线播放免费| 国产精彩免费视频| 国产亚洲在线观看| 日本人体一区二区| 亚洲视频久久| 久久综合亚洲精品| 伊人久久大香线蕉综合四虎小说 | 91麻豆精品成人一区二区| 久久久久久久网| 亚洲av无码国产精品久久| 不卡电影一区二区三区| 日本50路肥熟bbw| 懂色av中文一区二区三区| 欧美日韩理论片| 狠狠色综合日日| 手机看片国产精品| 国产精品影视在线观看| 日韩欧美理论片| 国产综合久久久久久久久久久久| 日日干夜夜操s8| 免费成人美女在线观看.| 精品亚洲一区二区三区四区| 免费人成黄页网站在线一区二区| 超碰影院在线观看| 天堂久久一区二区三区| 中文字幕天天干| 免费精品视频在线| 伊人成人222| 韩国毛片一区二区三区| 日日夜夜精品视频免费观看| 国产专区综合网| 中文字幕亚洲日本| a在线欧美一区| 欧美高清性xxxx| 国产欧美日韩综合精品一区二区| 懂色av蜜桃av| 亚洲日本青草视频在线怡红院| 亚洲欧美一区二区三区四区五区| 亚洲一区二区三区国产| 日本三级理论片| 日韩欧美第一页| 亚洲天堂网在线视频| 7777精品伊人久久久大香线蕉经典版下载 | 亚洲乱码国产乱码精品精天堂| 欧美69xxxxx| 少妇高潮久久久久久潘金莲| 国产欧美黑人| 97国产真实伦对白精彩视频8| 蜜臀国产一区| 91久久精品国产91性色| 一区二区三区亚洲变态调教大结局| 国产日韩三区| 成人在线免费视频观看| 国风产精品一区二区| 99视频+国产日韩欧美| 久草在在线视频| 国产高清成人在线| 无码人妻精品一区二区三应用大全| 国产日韩欧美精品在线| 在线免费观看亚洲视频| 婷婷开心激情综合| 亚洲中文字幕在线一区| 亚洲第一av在线| av网站大全在线观看| 国内精品在线一区| 日韩漫画puputoon| 国产精品青青草| 色婷婷综合网| 波多野结衣乳巨码无在线| 久久精品国产99国产精品| 丝袜熟女一区二区三区 | 在线不卡视频一区二区| 亚洲免费播放| 国产精品探花在线播放| 久久综合九色综合97婷婷女人| 三上悠亚在线观看视频| 欧美日韩另类在线| 99热这里只有精品在线| 亚洲欧美国内爽妇网| 在线观看av免费| 国产精品白嫩初高中害羞小美女| 91亚洲无吗| 一区精品在线| 丝袜诱惑制服诱惑色一区在线观看 | 国产性天天综合网| 久久久久久天堂| 7777精品伊人久久久大香线蕉| 四虎影视在线播放| 欧美国产日韩一区二区在线观看 | 国产盗摄——sm在线视频| 91美女片黄在线观看游戏| 国产欧美日韩精品一区二区免费| 成人黄色大片网站| 国产自产高清不卡| 男女男精品视频网站| 欧美色播在线播放| 天堂网在线资源| 久久99久久99精品免观看粉嫩| 欧美综合社区国产| 日韩av一区二区三区在线观看| 99视频一区| 国产女人18毛片水真多18| 伊人婷婷欧美激情| 国产特黄一级片| 久久色精品视频| 日本免费一区二区三区等视频| 日本视频精品一区| 日韩国产在线观看一区| 女尊高h男高潮呻吟| 欧美日韩在线观看视频| 天堂在线中文| 欧美综合一区第一页| 性人久久久久| 日韩avxxx| 久久先锋资源网| 69国产精品视频免费观看| 日韩国产欧美区| 蜜桃av在线| 免费看成人午夜电影| 先锋影音久久| 永久免费成人代码| 在线一区二区三区四区| 国产在线一在线二| 国产精品无av码在线观看| 日本成人小视频| 亚洲欧美aaa| 一区二区三区精品视频在线| 亚洲国产精品国自产拍久久| 久久久免费电影| 亲子伦视频一区二区三区| 中文字幕乱码人妻综合二区三区| 久久久久亚洲蜜桃| 中文字幕人妻互换av久久| 久久精品视频播放| 一区二区三区四区精品视频| 2018国产在线| 91麻豆国产精品久久| 高潮毛片又色又爽免费| 中文字幕在线国产精品| 亚洲一区有码| 毛片av在线播放| 26uuu亚洲婷婷狠狠天堂| 最近中文字幕免费观看| 久久精品视频在线播放| 51亚洲精品| 欧美成人黑人猛交| 亚洲欧洲无码一区二区三区| 国产刺激高潮av| 日韩美女视频免费看| 91精品国产视频| 亚洲精品乱码久久| 欧洲精品中文字幕| 九义人在线观看完整免费版电视剧| 不卡一卡2卡3卡4卡精品在| 亚洲欧美不卡| 日韩激情综合网| 日韩高清有码在线| 欧美网站免费| 国产男女免费视频| 国产精品免费人成网站| 亚洲卡一卡二卡三| 国产精品高潮呻吟久久av无限 | 欧美国产乱视频| 精品视频免费在线观看| 国产一级二级av| 色偷偷久久一区二区三区| 性网站在线观看| 日韩高清专区| 高清国产一区二区| 一级特黄aaaaaa大片| 国内精品小视频在线观看| 五月婷婷六月综合| 超碰97人人干| 欧美mv日韩mv| 欧美美女福利视频|