<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JAVA學(xué)習(xí)點(diǎn)點(diǎn)滴滴

    用開(kāi)放的腦子去闖蕩;用開(kāi)闊的視野去拼搏;用平和的身心去磨練;用美好的理想去追求!

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      18 隨筆 :: 1 文章 :: 2 評(píng)論 :: 0 Trackbacks

    李濤,Sun中國(guó)工程研究院工程師

    概述

    1:三個(gè)新加的多線程包

    2Callable Future接口

    3:新的線程執(zhí)行架構(gòu)

    4LockersCondition接口

    5: Synchronizer:同步裝置

    6: BlockingQueue接口

    7Atomics 原子級(jí)變量

    8Concurrent Collections 共點(diǎn)聚集

    概述

    Java 1995 年面世以來(lái)得到了廣泛得一個(gè)運(yùn)用,但是對(duì)多線程編程的支持 Java 很長(zhǎng)時(shí)間一直停留在初級(jí)階段。在 Java 5.0 之前 Java 里的多線程編程主要是通過(guò) Thread 類(lèi), Runnable 接口, Object 對(duì)象中的 wait() notify() 、 notifyAll() 等方法和 synchronized 關(guān)鍵詞來(lái)實(shí)現(xiàn)的。這些工具雖然能在大多數(shù)情況下解決對(duì)共享資源的管理和線程間的調(diào)度,但存在以下幾個(gè)問(wèn)題

    1.????? 過(guò)于原始,拿來(lái)就能用的功能有限,即使是要實(shí)現(xiàn)簡(jiǎn)單的多線程功能也需要編寫(xiě)大量的代碼。這些工具就像匯編語(yǔ)言一樣難以學(xué)習(xí)和使用,比這更糟糕的是稍有不慎它們還可能被錯(cuò)誤地使用,而且這樣的錯(cuò)誤很難被發(fā)現(xiàn)。

    2.????? 如果使用不當(dāng),會(huì)使程序的運(yùn)行效率大大降低。

    3.????? 為了提高開(kāi)發(fā)效率,簡(jiǎn)化編程,開(kāi)發(fā)人員在做項(xiàng)目的時(shí)候往往需要寫(xiě)一些共享的工具來(lái)實(shí)現(xiàn)一些普遍適用的功能。但因?yàn)闆](méi)有規(guī)范,相同的工具會(huì)被重復(fù)地開(kāi)發(fā),造成資源浪費(fèi)。

    4.????? 因?yàn)殒i定的功能是通過(guò) Synchronized 來(lái)實(shí)現(xiàn)的,這是一種塊結(jié)構(gòu),只能對(duì)代碼中的一段代碼進(jìn)行鎖定,而且鎖定是單一的。如以下代碼所示:

    synchronized lock {

    ? ?? // 執(zhí)行對(duì)共享資源的操作

    ??? ……

    }

    ? 一些復(fù)雜的功能就很難被實(shí)現(xiàn)。比如說(shuō)如果程序需要取得 lock A lock B 來(lái)進(jìn)行操作 1 ,然后需要取得 lock C 并且釋放 lock A 來(lái)進(jìn)行操作 2 Java 5.0 之前的多線程框架就顯得無(wú)能為力了。

    因?yàn)檫@些問(wèn)題,程序員對(duì)舊的框架一直頗有微詞。這種情況一直到 Java 5.0 才有較大的改觀,一系列的多線程工具包被納入了標(biāo)準(zhǔn)庫(kù)文件。這些工具包括了一個(gè)新的多線程程序的執(zhí)行框架,使編程人員可方便地協(xié)調(diào)和調(diào)度線程的運(yùn)行,并且新加入了一些高性能的常用的工具,使程序更容易編寫(xiě),運(yùn)行效率更高。本文將分類(lèi)并結(jié)合例子來(lái)介紹這些新加的多線程工具。

    在我們開(kāi)始介紹 Java 5.0 里的新 Concurrent 工具前讓我們先來(lái)看一下一個(gè)用舊的多線程工具編寫(xiě)的程序,這個(gè)程序里有一個(gè) Server 線程,它需要啟動(dòng)兩個(gè) Component , Server 線程需等到 Component 線程完畢后再繼續(xù)。相同的功能在 Synchronizer 一章里用新加的工具 CountDownLatch 有相同的實(shí)現(xiàn)。兩個(gè)程序,孰優(yōu)孰劣,哪個(gè)程序更容易編寫(xiě),哪個(gè)程序更容易理解,相信大家看過(guò)之后不難得出結(jié)論。

    public class ServerThread {

    ????? Object concLock = new Object();

    ????? int count = 2;

    public void runTwoThreads() {

    ????? // 啟動(dòng)兩個(gè)線程去初始化組件

    ??????????? new Thread(new ComponentThread1(this)).start();

    ??????????? new Thread(new ComponentThread1(this)).start();

    ??????????? // Wait for other thread

    while(count != 0) {

    ????????????????? synchronized(concLock) {

    ??????????????????????? try {

    ????????????????????????????? concLock.wait();

    ????????????????????????????? System.out.println("Wake up.");

    ??????????????????????? } catch (InterruptedException ie) { // 處理異常 }

    ????????????????? }

    ??????????? }

    ??????????? System.out.println("Server is up.");

    ????? }

    ????? public void callBack() {

    synchronized(concLock) {

    ????????????????? count--;

    ????????????????? concLock.notifyAll();

    ??????????? }

    ????? }

    ????? public static void main(String[] args){

    ??????????? ServerThread server = new ServerThread();

    ??????????? server.runTwoThreads();

    ????? }

    }

    ?

    public class ComponentThread1 implements Runnable {

    ????? private ServerThread server;

    ????? public ComponentThread1(ServerThread server) {

    ??????????? this.server = server;

    ????? }

    public void run() {

    ????? // 做組件初始化的工作

    ??????????? System.out.println("Do component initialization.");

    ??????????? server.callBack();

    ????? }

    }

    1:三個(gè)新加的多線程包

    Java 5.0 里新加入了三個(gè)多線程包: java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.

    • java.util.concurrent 包含了常用的多線程工具,是新的多線程工具的主體。
    • java.util.concurrent.atomic 包含了不用加鎖情況下就能改變值的原子變量,比如說(shuō) AtomicInteger 提供了 addAndGet() 方法。 Add Get 是兩個(gè)不同的操作,為了保證別的線程不干擾,以往的做法是先鎖定共享的變量,然后在鎖定的范圍內(nèi)進(jìn)行兩步操作。但用 AtomicInteger.addAndGet() 就不用擔(dān)心鎖定的事了,其內(nèi)部實(shí)現(xiàn)保證了這兩步操作是在原子量級(jí)發(fā)生的,不會(huì)被別的線程干擾。
    • java.util.concurrent.locks 包包含鎖定的工具。

    2Callable Future接口

    Callable 是類(lèi)似于 Runnable 的接口,實(shí)現(xiàn) Callable 接口的類(lèi)和實(shí)現(xiàn) Runnable 的類(lèi)都是可被其它線程執(zhí)行的任務(wù)。 Callable Runnable 有幾點(diǎn)不同:

    • Callable 規(guī)定的方法是 call() ,而 Runnable 規(guī)定的方法是 run().
    • Callable 的任務(wù)執(zhí)行后可返回值,而 Runnable 的任務(wù)是不能返回值的。
    • call ()方法可拋出異常,而 run ()方法是不能拋出異常的。
    • 運(yùn)行 Callable 任務(wù)可拿到一個(gè) Future 對(duì)象,通過(guò) Future 對(duì)象可了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取任務(wù)執(zhí)行的結(jié)果。

    以下是 Callable 的一個(gè)例子:

    public class DoCallStuff implements Callable<String>{ // *1

    ??????? private int aInt;

    ??????? public DoCallStuff(int aInt) {

    ??????????????? this.aInt = aInt;

    ??????? }

    ??????? public String call() throws Exception { //*2

    ??????????????? boolean resultOk = false;

    ??????????????? if(aInt == 0){

    ??????????????????????? resultOk = true;

    ??????????????? }? else if(aInt == 1){

    ??????????????????????? while(true){ //infinite loop

    ?????????????????? ?????????????System.out.println("looping....");

    ??????????????????????????????? Thread.sleep(3000);

    ??????????????????????? }

    ??????????????? } else {

    ??????????????????????? throw new Exception("Callable terminated with Exception!"); //*3

    ??????????????? }

    ??????????????? if(resultOk){

    ??????????????????????? return "Task done.";

    ??????????????? } else {

    ??????????????????????? return "Task failed";

    ??????????????? }

    ??????? }

    }

    *1: 名為 DoCallStuff 類(lèi)實(shí)現(xiàn)了 Callable<String> , String 將是 call 方法的返回值類(lèi)型。例子中用了 String ,但可以是任何 Java 類(lèi)。

    *2: call 方法的返回值類(lèi)型為 String ,這是和類(lèi)的定義相對(duì)應(yīng)的。并且可以拋出異常。

    *3: call 方法可以拋出異常,如加重的斜體字所示。

    以下是調(diào)用 DoCallStuff 的主程序。

    import java.util.concurrent.ExecutionException;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.Future;

    public class Executor {

    ??????? public static void main(String[] args){

    ??????????????? //*1

    ??????????????? DoCallStuff call1 = new DoCallStuff(0);

    ??????????????? DoCallStuff call2 = new DoCallStuff(1);

    ??????????????? DoCallStuff call3 = new DoCallStuff(2);

    ??????????????? //*2

    ??????????????? ExecutorService es = Executors.newFixedThreadPool(3);

    ??????????????? //*3

    ??????????????? Future<String> future1 = es.submit(call1);

    ??????????????? Future<String> future2 = es.submit(call2);

    ???? ???????????Future<String> future3 = es.submit(call3);

    ??????????????? try {

    ??????????????????????? //*4

    ??????????????????????? System.out.println(future1.get());

    ???????????????????????? //*5

    ??????????????????????? Thread.sleep(3000);

    ????????????????? ??????System.out.println("Thread 2 terminated? :" + future2.cancel(true));

    ??????????????????????? //*6

    ??????????????????????? System.out.println(future3.get());

    ??????????????? } catch (ExecutionException ex) {

    ??????????????????????? ex.printStackTrace();

    ??????????????? } catch (InterruptedException ex) {

    ??????????????????????? ex.printStackTrace();

    ??????????????? }

    ??????? }

    }

    *1: 定義了幾個(gè)任務(wù)

    *2: 初始了任務(wù)執(zhí)行工具。任務(wù)的執(zhí)行框架將會(huì)在后面解釋。

    *3: 執(zhí)行任務(wù),任務(wù)啟動(dòng)時(shí)返回了一個(gè) Future 對(duì)象,如果想得到任務(wù)執(zhí)行的結(jié)果或者是異??蓪?duì)這個(gè) Future 對(duì)象進(jìn)行操作。 Future 所含的值必須跟 Callable 所含的值對(duì)映,比如說(shuō)例子中 Future<String> 對(duì)印 Callable<String>

    *4: 任務(wù) 1 正常執(zhí)行完畢, future1.get() 會(huì)返回線程的值

    *5: 任務(wù) 2 在進(jìn)行一個(gè)死循環(huán),調(diào)用 future2.cancel(true) 來(lái)中止此線程。傳入的參數(shù)標(biāo)明是否可打斷線程, true 表明可以打斷。

    *6: 任務(wù) 3 拋出異常,調(diào)用 future3.get() 時(shí)會(huì)引起異常的拋出。

    ? 運(yùn)行 Executor 會(huì)有以下運(yùn)行結(jié)果:

    looping....

    Task done. //*1

    looping....

    looping....//*2

    looping....

    looping....

    looping....

    looping....

    Thread 2 terminated? :true //*3

    //*4

    java.util.concurrent.ExecutionException: java.lang.Exception: Callable terminated with Exception!

    ??????? at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:205)

    ??????? at java.util.concurrent.FutureTask.get(FutureTask.java:80)

    ??????? at concurrent.Executor.main(Executor.java:43)

    ??????? …….

    *1: 任務(wù) 1 正常結(jié)束

    *2: 任務(wù) 2 是個(gè)死循環(huán),這是它的打印結(jié)果

    *3: 指示任務(wù) 2 被取消

    *4: 在執(zhí)行 future3.get() 時(shí)得到任務(wù) 3 拋出的異常

    3:新的任務(wù)執(zhí)行架構(gòu)

    Java 5.0 之前啟動(dòng)一個(gè)任務(wù)是通過(guò)調(diào)用 Thread 類(lèi)的 start() 方法來(lái)實(shí)現(xiàn)的,任務(wù)的提于交和執(zhí)行是同時(shí)進(jìn)行的,如果你想對(duì)任務(wù)的執(zhí)行進(jìn)行調(diào)度或是控制同時(shí)執(zhí)行的線程數(shù)量就需要額外編寫(xiě)代碼來(lái)完成。 5.0 里提供了一個(gè)新的任務(wù)執(zhí)行架構(gòu)使你可以輕松地調(diào)度和控制任務(wù)的執(zhí)行,并且可以建立一個(gè)類(lèi)似數(shù)據(jù)庫(kù)連接池的線程池來(lái)執(zhí)行任務(wù)。這個(gè)架構(gòu)主要有三個(gè)接口和其相應(yīng)的具體類(lèi)組成。這三個(gè)接口是 Executor, ExecutorService ScheduledExecutorService ,讓我們先用一個(gè)圖來(lái)顯示它們的關(guān)系:

    ?

    圖的左側(cè)是接口,圖的右側(cè)是這些接口的具體類(lèi)。注意 Executor 是沒(méi)有直接具體實(shí)現(xiàn)的。

    Executor 接口:

    是用來(lái)執(zhí)行 Runnable 任務(wù)的,它只定義一個(gè)方法:

    • execute(Runnable command) :執(zhí)行 Ruannable 類(lèi)型的任務(wù)

    ExecutorService 接口:

    ExecutorService 繼承了 Executor 的方法,并提供了執(zhí)行 Callable 任務(wù)和中止任務(wù)執(zhí)行的服務(wù),其定義的方法主要有:

    • submit(task) :可用來(lái)提交 Callable Runnable 任務(wù),并返回代表此任務(wù)的 Future 對(duì)象
    • invokeAll(collection of tasks) :批處理任務(wù)集合,并返回一個(gè)代表這些任務(wù)的 Future 對(duì)象集合
    • shutdown() :在完成已提交的任務(wù)后關(guān)閉服務(wù),不再接受新任務(wù)
    • shutdownNow() :停止所有正在執(zhí)行的任務(wù)并關(guān)閉服務(wù)。
    • isTerminated() :測(cè)試是否所有任務(wù)都執(zhí)行完畢了。
    • isShutdown() :測(cè)試是否該 ExecutorService 已被關(guān)閉

    ScheduledExecutorService 接口

    ExecutorService 的基礎(chǔ)上, ScheduledExecutorService 提供了按時(shí)間安排執(zhí)行任務(wù)的功能,它提供的方法主要有:

    • schedule(task, initDelay): 安排所提交的 Callable Runnable 任務(wù)在 initDelay 指定的時(shí)間后執(zhí)行。
    • scheduleAtFixedRate() :安排所提交的 Runnable 任務(wù)按指定的間隔重復(fù)執(zhí)行
    • scheduleWithFixedDelay() :安排所提交的 Runnable 任務(wù)在每次執(zhí)行完后,等待 delay 所指定的時(shí)間后重復(fù)執(zhí)行。

    代碼: ScheduleExecutorService 的例子

    public class ScheduledExecutorServiceTest {

    ??????? public static void main(String[] args)

    ?????????????? throws InterruptedException, ExecutionException{

    ?????????????? //*1

    ??????????????? ScheduledExecutorService service = Executors.newScheduledThreadPool(2);

    ??????????????? //*2

    ??????????????? Runnable task1 = new Runnable() {

    ???????????????????? public void run() {

    ??????????????????????? System.out.println("Task repeating.");

    ???????????????????? }

    ??????????????? };

    ??????????????? //*3

    ??????????????? final ScheduledFuture future1 =

    ??????????????????????? service.scheduleAtFixedRate(task1, 0, 1, TimeUnit.SECONDS);

    ??????????????? //*4

    ??????????????? ScheduledFuture<String> future2 = service.schedule(new Callable<String>(){

    ???????????????????? public String call(){

    ???????????????????????????? future1.cancel(true);

    ???????????????????????????? return "task cancelled!";

    ??????????????? ?????}

    ??????????????? }, 5, TimeUnit.SECONDS);

    ??????????????? System.out.println(future2.get());

    //*5

    service.shutdown();

    ??????? }

    }

    這個(gè)例子有兩個(gè)任務(wù),第一個(gè)任務(wù)每隔一秒打印一句“ Task repeating , 第二個(gè)任務(wù)在 5 秒鐘后取消第一個(gè)任務(wù)。

    *1: 初始化一個(gè) ScheduledExecutorService 對(duì)象,這個(gè)對(duì)象的線程池大小為 2

    *2: 用內(nèi)函數(shù)的方式定義了一個(gè) Runnable 任務(wù)。

    *3: 調(diào)用所定義的 ScheduledExecutorService 對(duì)象來(lái)執(zhí)行任務(wù),任務(wù)每秒執(zhí)行一次。能重復(fù)執(zhí)行的任務(wù)一定是 Runnable 類(lèi)型。注意我們可以用 TimeUnit 來(lái)制定時(shí)間單位,這也是 Java 5.0 里新的特征, 5.0 以前的記時(shí)單位是微秒,現(xiàn)在可精確到奈秒。

    *4: 調(diào)用 ScheduledExecutorService 對(duì)象來(lái)執(zhí)行第二個(gè)任務(wù),第二個(gè)任務(wù)所作的就是在 5 秒鐘后取消第一個(gè)任務(wù)。

    *5: 關(guān)閉服務(wù)。

    Executors 類(lèi)

    雖然以上提到的接口有其實(shí)現(xiàn)的具體類(lèi),但為了方便 Java 5.0 建議使用 Executors 的工具類(lèi)來(lái)得到 Executor 接口的具體對(duì)象,需要注意的是 Executors 是一個(gè)類(lèi),不是 Executor 的復(fù)數(shù)形式。 Executors 提供了以下一些 static 的方法:

    • callable(Runnable task): Runnable 的任務(wù)轉(zhuǎn)化成 Callable 的任務(wù)
    • newSingleThreadExecutor: 產(chǎn)生一個(gè) ExecutorService 對(duì)象,這個(gè)對(duì)象只有一個(gè)線程可用來(lái)執(zhí)行任務(wù),若任務(wù)多于一個(gè),任務(wù)將按先后順序執(zhí)行。
    • newCachedThreadPool(): 產(chǎn)生一個(gè) ExecutorService 對(duì)象,這個(gè)對(duì)象帶有一個(gè)線程池,線程池的大小會(huì)根據(jù)需要調(diào)整,線程執(zhí)行完任務(wù)后返回線程池,供執(zhí)行下一次任務(wù)使用。
    • newFixedThreadPool(int poolSize) :產(chǎn)生一個(gè) ExecutorService 對(duì)象,這個(gè)對(duì)象帶有一個(gè)大小為 poolSize 的線程池,若任務(wù)數(shù)量大于 poolSize ,任務(wù)會(huì)被放在一個(gè) queue 里順序執(zhí)行。
    • newSingleThreadScheduledExecutor :產(chǎn)生一個(gè) ScheduledExecutorService 對(duì)象,這個(gè)對(duì)象的線程池大小為 1 ,若任務(wù)多于一個(gè),任務(wù)將按先后順序執(zhí)行。
    • newScheduledThreadPool(int poolSize): 產(chǎn)生一個(gè) ScheduledExecutorService 對(duì)象,這個(gè)對(duì)象的線程池大小為 poolSize ,若任務(wù)數(shù)量大于 poolSize ,任務(wù)會(huì)在一個(gè) queue 里等待執(zhí)行

    以下是得到和使用 ExecutorService 的例子:

    代碼:如何調(diào)用 Executors 來(lái)獲得各種服務(wù)對(duì)象

    //Single Threaded ExecutorService

    ???? ExecutorService singleThreadeService = Executors.newSingleThreadExecutor();

    //Cached ExecutorService

    ???? ExecutorService cachedService = Executors.newCachedThreadPool();

    //Fixed number of ExecutorService

    ???? ExecutorService fixedService = Executors.newFixedThreadPool(3);

    //Single ScheduledExecutorService

    ???? ScheduledExecutorService singleScheduledService =

    ????????? Executors.newSingleThreadScheduledExecutor();

    //Fixed number of ScheduledExecutorService

    ScheduledExecutorService fixedScheduledService =

    ???? Executors.newScheduledThreadPool(3);

    4LockersCondition接口

    在多線程編程里面一個(gè)重要的概念是鎖定,如果一個(gè)資源是多個(gè)線程共享的,為了保證數(shù)據(jù)的完整性,在進(jìn)行事務(wù)性操作時(shí)需要將共享資源鎖定,這樣可以保證在做事務(wù)性操作時(shí)只有一個(gè)線程能對(duì)資源進(jìn)行操作,從而保證數(shù)據(jù)的完整性。在 5.0 以前,鎖定的功能是由 Synchronized 關(guān)鍵字來(lái)實(shí)現(xiàn)的,這樣做存在幾個(gè)問(wèn)題:

    • 每次只能對(duì)一個(gè)對(duì)象進(jìn)行鎖定。若需要鎖定多個(gè)對(duì)象,編程就比較麻煩,一不小心就會(huì)出現(xiàn)死鎖現(xiàn)象。
    • 如果線程因拿不到鎖定而進(jìn)入等待狀況,是沒(méi)有辦法將其打斷的

    Java 5.0 里出現(xiàn)兩種鎖的工具可供使用,下圖是這兩個(gè)工具的接口及其實(shí)現(xiàn):

    Lock 接口

    ReentrantLock Lock 的具體類(lèi), Lock 提供了以下一些方法:

    • lock(): 請(qǐng)求鎖定,如果鎖已被別的線程鎖定,調(diào)用此方法的線程被阻斷進(jìn)入等待狀態(tài)。
    • tryLock() :如果鎖沒(méi)被別的線程鎖定,進(jìn)入鎖定狀態(tài),并返回 true 。若鎖已被鎖定,返回 false ,不進(jìn)入等待狀態(tài)。此方法還可帶時(shí)間參數(shù),如果鎖在方法執(zhí)行時(shí)已被鎖定,線程將繼續(xù)等待規(guī)定的時(shí)間,若還不行才返回 false 。
    • unlock() :取消鎖定,需要注意的是 Lock 不會(huì)自動(dòng)取消,編程時(shí)必須手動(dòng)解鎖。

    代碼:

    // 生成一個(gè)鎖

    Lock lock = new ReentrantLock();

    public void accessProtectedResource() {

    ? lock.lock(); // 取得鎖定

    ? try {

    ??? // 對(duì)共享資源進(jìn)行操作

    ? } finally {

    ??? // 一定記著把鎖取消掉,鎖本身是不會(huì)自動(dòng)解鎖的

    ??? lock.unlock() ;

    ? }

    }

    ReadWriteLock 接口

    為了提高效率有些共享資源允許同時(shí)進(jìn)行多個(gè)讀的操作,但只允許一個(gè)寫(xiě)的操作,比如一個(gè)文件,只要其內(nèi)容不變可以讓多個(gè)線程同時(shí)讀,不必做排他的鎖定,排他的鎖定只有在寫(xiě)的時(shí)候需要,以保證別的線程不會(huì)看到數(shù)據(jù)不完整的文件。 ReadWriteLock 可滿足這種需要。 ReadWriteLock 內(nèi)置兩個(gè) Lock ,一個(gè)是讀的 Lock ,一個(gè)是寫(xiě)的 Lock 。多個(gè)線程可同時(shí)得到讀的 Lock ,但只有一個(gè)線程能得到寫(xiě)的 Lock ,而且寫(xiě)的 Lock 被鎖定后,任何線程都不能得到 Lock 。 ReadWriteLock 提供的方法有:

    • readLock(): 返回一個(gè)讀的 lock
    • writeLock(): 返回一個(gè)寫(xiě)的 lock, lock 是排他的。

    ReadWriteLock 的例子:

    public class FileOperator{

    ????? // 初始化一個(gè) ReadWriteLock

    ????? ReadWriteLock lock = new ReentrantReadWriteLock();

    public String read() {

    ????? // 得到 readLock 并鎖定

    ??????????? Lock readLock = lock.readLock();

    ?????? ?????readLock.lock();

    ??????????? try {

    ????????????????? // 做讀的工作

    ????????????????? return "Read something";

    ??????????? } finally {

    ???????????????? readLock.unlock();

    ??????????? }

    ????? }

    ?????

    public void write(String content) {

    ????? // 得到 writeLock 并鎖定

    ??????????? Lock writeLock = lock.writeLock();

    ??????????? writeLock.lock();

    ??????????? try {

    ????????????????? // 做讀的工作

    ??????????? } finally {

    ???????????????? writeLock.unlock();

    ??????????? }

    ????? }

    }

    ?

    需要注意的是 ReadWriteLock 提供了一個(gè)高效的鎖定機(jī)理,但最終程序的運(yùn)行效率是和程序的設(shè)計(jì)息息相關(guān)的,比如說(shuō)如果讀的線程和寫(xiě)的線程同時(shí)在等待,要考慮是先發(fā)放讀的 lock 還是先發(fā)放寫(xiě)的 lock 。如果寫(xiě)發(fā)生的頻率不高,而且快,可以考慮先給寫(xiě)的 lock 。還要考慮的問(wèn)題是如果一個(gè)寫(xiě)正在等待讀完成,此時(shí)一個(gè)新的讀進(jìn)來(lái),是否要給這個(gè)新的讀發(fā)鎖,如果發(fā)了,可能導(dǎo)致寫(xiě)的線程等很久。等等此類(lèi)問(wèn)題在編程時(shí)都要給予充分的考慮。

    Condition 接口:

    有時(shí)候線程取得 lock 后需要在一定條件下才能做某些工作,比如說(shuō)經(jīng)典的 Producer Consumer 問(wèn)題, Consumer 必須在籃子里有蘋(píng)果的時(shí)候才能吃蘋(píng)果,否則它必須暫時(shí)放棄對(duì)籃子的鎖定,等到 Producer 往籃子里放了蘋(píng)果后再去拿來(lái)吃。而 Producer 必須等到籃子空了才能往里放蘋(píng)果,否則它也需要暫時(shí)解鎖等 Consumer 把蘋(píng)果吃了才能往籃子里放蘋(píng)果。在 Java 5.0 以前,這種功能是由 Object 類(lèi)的 wait(), notify() notifyAll() 等方法實(shí)現(xiàn)的,在 5.0 里面,這些功能集中到了 Condition 這個(gè)接口來(lái)實(shí)現(xiàn), Condition 提供以下方法:

    • await() :使調(diào)用此方法的線程放棄鎖定,進(jìn)入睡眠直到被打斷或被喚醒。
    • signal(): 喚醒一個(gè)等待的線程
    • signalAll() :?jiǎn)拘阉械却木€程

    Condition 的例子:

    public class Basket {?????

    Lock lock = new ReentrantLock();

    // 產(chǎn)生 Condition 對(duì)象

    ???? Condition produced = lock.newCondition();

    ???? Condition consumed = lock.newCondition();

    ???? boolean available = false;

    ?? ??

    ???? public void produce() throws InterruptedException {

    ?????????? lock.lock();

    ?????????? try {

    ???????????????? if(available){

    ??????????????????? consumed.await(); // 放棄 lock 進(jìn)入睡眠 ?

    ???????????????? }

    ???????????????? /* 生產(chǎn)蘋(píng)果 */

    ???????????????? System.out.println("Apple produced.");

    ???????????????? available = true;

    ???????????????? produced.signal(); // 發(fā)信號(hào)喚醒等待這個(gè) Condition 的線程

    ?????????? } finally {

    ???????????????? lock.unlock();

    ?????????? }

    ???? }

    ????

    ???? public void consume() throws InterruptedException {

    ?????????? lock.lock();

    ?????????? try {

    ???????????????? if(!available){

    ?????????????????????? produced.await();// 放棄 lock 進(jìn)入睡眠 ?

    ???????????????? }

    ???????????????? /* 吃蘋(píng)果 */

    ???????????????? System.out.println("Apple consumed.");

    ???????????????? available = false;

    ???????????????? consumed.signal();// 發(fā)信號(hào)喚醒等待這個(gè) Condition 的線程

    ?????????? } finally {

    ???????????????? lock.unlock();

    ?????????? }

    ???? }?????

    }

    ConditionTester:

    public class ConditionTester {

    ?????

    ????? public static void main(String[] args) throws InterruptedException{

    final Basket basket = new Basket();

    // 定義一個(gè) producer

    ??????????? Runnable producer = new Runnable() {

    ????????????????? public void run() {

    ??????????????????????? try {

    ????????????????????????????? basket.produce();

    ?????? ?????????????????} catch (InterruptedException ex) {

    ????????????????????????????? ex.printStackTrace();

    ??????????????????????? }

    ????????????????? }

    };

    // 定義一個(gè) consumer

    ??????????? Runnable consumer = new Runnable() {

    ????????????????? public void run() {

    ??????????????????????? try {

    ????????????????????????????? basket.consume();

    ??????????????????????? } catch (InterruptedException ex) {

    ????????????????????????????? ex.printStackTrace();

    ??????????????????????? }

    ????????????????? }

    };

    // 各產(chǎn)生 10 個(gè) consumer producer

    ??????????? ExecutorService service = Executors.newCachedThreadPool();

    ??????????? for(int i=0; i < 10; i++)

    ????????????????? service.submit(consumer);

    ??????????? Thread.sleep(2000);

    ??????????? for(int i=0; i<10; i++)

    ????????????????? service.submit(producer);

    ??????????? service.shutdown();

    ????? }?????

    }

    5: Synchronizer:同步裝置

    Java 5.0 里新加了 4 個(gè)協(xié)調(diào)線程間進(jìn)程的同步裝置,它們分別是 Semaphore, CountDownLatch, CyclicBarrier Exchanger.

    Semaphore:

    用來(lái)管理一個(gè)資源池的工具, Semaphore 可以看成是個(gè)通行證,線程要想從資源池拿到資源必須先拿到通行證, Semaphore 提供的通行證數(shù)量和資源池的大小一致。如果線程暫時(shí)拿不到通行證,線程就會(huì)被阻斷進(jìn)入等待狀態(tài)。以下是一個(gè)例子:

    public class Pool {

    ??? ??ArrayList<String> pool = null;

    ????? Semaphore pass = null;

    ????? public Pool(int size){

    ??????????? // 初始化資源池

    ??????????? pool = new ArrayList<String>();

    ??????????? for(int i=0; i<size; i++){

    ????????????????? pool.add("Resource "+i);

    ??????????? }

    ??? ???????? //Semaphore 的大小和資源池的大小一致

    ??????????? pass = new Semaphore(size);

    ????? }

    ????? public String get() throws InterruptedException{

    ??????????? // 獲取通行證 , 只有得到通行證后才能得到資源

    ??????????? pass.acquire();

    ??????????? return getResource();

    ????? }

    ????? public void put(String resource){

    ??????????? // 歸還通行證,并歸還資源

    ??????????? pass.release();

    ??????????? releaseResource(resource);

    ????? }

    ???? private synchronized String getResource() {

    ??????????? String result = pool.get(0);

    ??????????? pool.remove(0);

    ??????????? System.out.println("Give out "+result);

    ??????????? return result;

    ????? }

    ????? private synchronized void releaseResource(String resource) {

    ??????????? System.out.println("return "+resource);

    ??????????? pool.add(resource);

    ????? }

    }

    SemaphoreTest:

    public class SemaphoreTest {

    ????? public static void main(String[] args){

    ??????????? final Pool aPool = new Pool(2);

    ??????????? Runnable worker = new Runnable() {

    ????????????????? public void run() {

    ??????????????????????? String resource = null;

    ??????? ????????????????try {

    ????????????????????????????? // 取得 resource

    ????????????????????????????? resource = aPool.get();

    ??????????????????????? } catch (InterruptedException ex) {

    ????????????????????????????? ex.printStackTrace();

    ??????????????????????? }

    ??????????????????????? // resource 做工作

    ??????????????????????? System.out.println("I worked on "+resource);

    ??????????????????????? // 歸還 resource

    ??????????????????????? aPool.put(resource);

    ????????????????? }

    ??????????? };

    ??????????? ExecutorService service = Executors.newCachedThreadPool();

    ??????????? for(int i=0; i<20; i++){

    ????????????????? service.submit(worker);

    ??????????? }

    ??????????? service.shutdown();

    ????? }????

    }

    CountDownLatch:

    CountDownLatch 是個(gè)計(jì)數(shù)器,它有一個(gè)初始數(shù),等待這個(gè)計(jì)數(shù)器的線程必須等到計(jì)數(shù)器倒數(shù)到零時(shí)才可繼續(xù)。比如說(shuō)一個(gè) Server 啟動(dòng)時(shí)需要初始化 4 個(gè)部件, Server 可以同時(shí)啟動(dòng) 4 個(gè)線程去初始化這 4 個(gè)部件,然后調(diào)用 CountDownLatch(4).await() 阻斷進(jìn)入等待,每個(gè)線程完成任務(wù)后會(huì)調(diào)用一次 CountDownLatch.countDown() 來(lái)倒計(jì)數(shù) , 當(dāng) 4 個(gè)線程都結(jié)束時(shí) CountDownLatch 的計(jì)數(shù)就會(huì)降低為 0 ,此時(shí) Server 就會(huì)被喚醒繼續(xù)下一步操作。 CountDownLatch 的方法主要有:

    • await() :使調(diào)用此方法的線程阻斷進(jìn)入等待
    • countDown(): 倒計(jì)數(shù),將計(jì)數(shù)值減 1
    • getCount(): 得到當(dāng)前的計(jì)數(shù)值

    CountDownLatch 的例子:一個(gè) server 調(diào)了三個(gè) ComponentThread 分別去啟動(dòng)三個(gè)組件,然后 server 等到組件都啟動(dòng)了再繼續(xù)。

    public class Server {

    ????? public static void main(String[] args) throws InterruptedException{

    ??????????? System.out.println("Server is starting.");

    ?????????? ?// 初始化一個(gè)初始值為 3 CountDownLatch

    ??????????? CountDownLatch latch = new CountDownLatch(3);

    ??????????? // 3 個(gè)線程分別去啟動(dòng) 3 個(gè)組件

    ??????????? ExecutorService service = Executors.newCachedThreadPool();

    ??????????? service.submit(new ComponentThread(latch, 1));

    ????????? ??service.submit(new ComponentThread(latch, 2));

    ??????????? service.submit(new ComponentThread(latch, 3));

    ??????????? service.shutdown();

    ??????????? // 進(jìn)入等待狀態(tài)

    ??????????? latch.await();

    ??????????? // 當(dāng)所需的三個(gè)組件都完成時(shí), Server 就可繼續(xù)了

    ??????????? System.out.println("Server is up!");

    ????? }

    }

    ?

    public class ComponentThread implements Runnable{

    ????? CountDownLatch latch;

    ????? int ID;

    ????? /** Creates a new instance of ComponentThread */

    ????? public ComponentThread(CountDownLatch latch, int ID) {

    ??????????? this.latch = latch;

    ??????????? this.ID = ID;

    ????? }

    ????? public void run() {

    ??????????? System.out.println("Component "+ID + " initialized!");

    ??????????? // 將計(jì)數(shù)減一

    ??????????? latch.countDown();

    ????? }????

    }

    運(yùn)行結(jié)果:

    Server is starting.

    Component 1 initialized!

    Component 3 initialized!

    Component 2 initialized!

    Server is up!

    CyclicBarrier:

    CyclicBarrier 類(lèi)似于 CountDownLatch 也是個(gè)計(jì)數(shù)器,不同的是 CyclicBarrier 數(shù)的是調(diào)用了 CyclicBarrier.await() 進(jìn)入等待的線程數(shù),當(dāng)線程數(shù)達(dá)到了 CyclicBarrier 初始時(shí)規(guī)定的數(shù)目時(shí),所有進(jìn)入等待狀態(tài)的線程被喚醒并繼續(xù)。 CyclicBarrier 就象它名字的意思一樣,可看成是個(gè)障礙,所有的線程必須到齊后才能一起通過(guò)這個(gè)障礙。 CyclicBarrier 初始時(shí)還可帶一個(gè) Runnable 的參數(shù),此 Runnable 任務(wù)在 CyclicBarrier 的數(shù)目達(dá)到后,所有其它線程被喚醒前被執(zhí)行。

    CyclicBarrier 提供以下幾個(gè)方法:

    • await() :進(jìn)入等待
    • getParties() :返回此 barrier 需要的線程數(shù)
    • reset() :將此 barrier 重置

    以下是使用 CyclicBarrier 的一個(gè)例子:兩個(gè)線程分別在一個(gè)數(shù)組里放一個(gè)數(shù),當(dāng)這兩個(gè)線程都結(jié)束后,主線程算出數(shù)組里的數(shù)的和(這個(gè)例子比較無(wú)聊,我沒(méi)有想到更合適的例子)

    public class MainThread {

    public static void main(String[] args)

    ????? throws InterruptedException, BrokenBarrierException, TimeoutException{

    ??????????? final int[] array = new int[2];

    ??????????? CyclicBarrier barrier = new CyclicBarrier(2,

    ????????????????? new Runnable() {// 在所有線程都到達(dá) Barrier 時(shí)執(zhí)行

    ????????????????? public void run() {

    ??????????????????????? System.out.println("Total is:"+(array[0]+array[1]));

    ????????????????? }

    ??????????? });???????????

    ??????????? // 啟動(dòng)線程

    ??????????? new Thread(new ComponentThread(barrier, array, 0)).start();

    ??????????? new Thread(new ComponentThread(barrier, array, 1)).start();???

    ????? }?????

    }

    ?

    public class ComponentThread implements Runnable{

    ????? CyclicBarrier barrier;

    ????? int ID;

    ????? int[] array;

    ????? public ComponentThread(CyclicBarrier barrier, int[] array, int ID) {

    ??????????? this.barrier = barrier;

    ??????????? this.ID = ID;

    ??????????? this.array = array;

    ????? }

    ????? public void run() {

    ??????????? try {

    ????????????????? array[ID] = new Random().nextInt();

    ????????????????? System.out.println(ID+ " generates:"+array[ID]);

    ????????????????? // 該線程完成了任務(wù)等在 Barrier

    ????????????????? barrier.await();

    ??????????? } catch (BrokenBarrierException ex) {

    ????????????????? ex.printStackTrace();

    ??????????? } catch (InterruptedException ex) {

    ????????????????? ex.printStackTrace();

    ??????????? }

    ????? }

    }

    Exchanger:

    顧名思義 Exchanger 讓兩個(gè)線程可以互換信息。用一個(gè)例子來(lái)解釋比較容易。例子中服務(wù)生線程往空的杯子里倒水,顧客線程從裝滿水的杯子里喝水,然后通過(guò) Exchanger 雙方互換杯子,服務(wù)生接著往空杯子里倒水,顧客接著喝水,然后交換,如此周而復(fù)始。

    class FillAndEmpty {

    ????? // 初始化一個(gè) Exchanger ,并規(guī)定可交換的信息類(lèi)型是 DataCup

    ????? Exchanger<Cup> exchanger = new Exchanger();

    ???? ?Cup initialEmptyCup = ...; // 初始化一個(gè)空的杯子

    ????? Cup initialFullCup = ...; // 初始化一個(gè)裝滿水的杯子

    ????? // 服務(wù)生線程

    ????? class Waiter implements Runnable {

    ??????????? public void run() {

    ????????????????? Cup currentCup = initialEmptyCup;

    ????????????????? try {

    ?????? ?????????????????// 往空的杯子里加水

    ??????????????????????? currentCup.addWater();

    ??????????????????????? // 杯子滿后和顧客的空杯子交換

    ??????????????????????? currentCup = exchanger.exchange(currentCup);

    ????????????????? } catch (InterruptedException ex) { ... handle ... }

    ? ?????????? }

    ????? }

    ????? // 顧客線程

    ????? class Customer implements Runnable {

    ??????????? public void run() {

    ????????????????? DataCup currentCup = initialFullCup;

    ????????????????? try {

    ??????????????????????? // 把杯子里的水喝掉

    ??????????????????????? currentCup.drinkFromCup();

    ??????????????????????? // 將空杯子和服務(wù)生的滿杯子交換

    ??????????????????????? currentCup = exchanger.exchange(currentCup);

    ????????????????? } catch (InterruptedException ex) { ... handle ...}

    ??????????? }

    ????? }

    ?????

    ????? void start() {

    ??????? ????new Thread(new Waiter()).start();

    ??????????? new Thread(new Customer()).start();

    ????? }

    }

    6: BlockingQueue接口

    BlockingQueue 是一種特殊的 Queue ,若 BlockingQueue 是空的,從 BlockingQueue 取東西的操作將會(huì)被阻斷進(jìn)入等待狀態(tài)直到 BlocingkQueue 進(jìn)了新貨才會(huì)被喚醒。同樣,如果 BlockingQueue 是滿的任何試圖往里存東西的操作也會(huì)被阻斷進(jìn)入等待狀態(tài),直到 BlockingQueue 里有新的空間才會(huì)被喚醒繼續(xù)操作。 BlockingQueue 提供的方法主要有:

    • add(anObject): anObject 加到 BlockingQueue 里,如果 BlockingQueue 可以容納返回 true ,否則拋出 IllegalStateException 異常。
    • offer(anObject) :把 anObject 加到 BlockingQueue 里,如果 BlockingQueue 可以容納返回 true ,否則返回 false 。
    • put(anObject) :把 anObject 加到 BlockingQueue 里,如果 BlockingQueue 沒(méi)有空間,調(diào)用此方法的線程被阻斷直到 BlockingQueue 里有新的空間再繼續(xù)。
    • poll(time) :取出 BlockingQueue 里排在首位的對(duì)象,若不能立即取出可等 time 參數(shù)規(guī)定的時(shí)間。取不到時(shí)返回 null 。
    • take() :取出 BlockingQueue 里排在首位的對(duì)象,若 BlockingQueue 為空,阻斷進(jìn)入等待狀態(tài)直到 BlockingQueue 有新的對(duì)象被加入為止。

    根據(jù)不同的需要 BlockingQueue 4 種具體實(shí)現(xiàn):

    • ArrayBlockingQueue :規(guī)定大小的 BlockingQueue ,其構(gòu)造函數(shù)必須帶一個(gè) int 參數(shù)來(lái)指明其大小。其所含的對(duì)象是以 FIFO (先入先出)順序排序的。
    • LinkedBlockingQueue :大小不定的 BlockingQueue ,若其構(gòu)造函數(shù)帶一個(gè)規(guī)定大小的參數(shù),生成的 BlockingQueue 有大小限制,若不帶大小參數(shù),所生成的 BlockingQueue 的大小由 Integer.MAX_VALUE 來(lái)決定。其所含的對(duì)象是以 FIFO (先入先出)順序排序的。 LinkedBlockingQueue ArrayBlockingQueue 比較起來(lái),它們背后所用的數(shù)據(jù)結(jié)構(gòu)不一樣,導(dǎo)致 LinkedBlockingQueue 的數(shù)據(jù)吞吐量要大于 ArrayBlockingQueue ,但在線程數(shù)量很大時(shí)其性能的可預(yù)見(jiàn)性低于 ArrayBlockingQueue 。
    • PriorityBlockingQueue :類(lèi)似于 LinkedBlockingQueue ,但其所含對(duì)象的排序不是 FIFO ,而是依據(jù)對(duì)象的自然排序順序或者是構(gòu)造函數(shù)所帶的 Comparator 決定的順序。
    • SynchronousQueue :特殊的 BlockingQueue ,對(duì)其的操作必須是放和取交替完成的。

    下面是用 BlockingQueue 來(lái)實(shí)現(xiàn) Producer Consumer 的例子:

    public class BlockingQueueTest {

    ????? static BlockingQueue<String> basket;

    ????? public BlockingQueueTest() {

    ??????????? // 定義了一個(gè)大小為 2 BlockingQueue ,也可根據(jù)需要用其他的具體類(lèi)

    ??????????? basket = new ArrayBlockingQueue<String>(2);

    ????? }

    ????? class Producor implements Runnable {

    ??????????? public void run() {

    ????????????????? while(true){

    ??????????????????????? try {

    ????????????????????????????? // 放入一個(gè)對(duì)象,若 basket 滿了,等到 basket 有位置

    ?????????????????????? ???????basket.put("An apple");

    ??????????????????????? } catch (InterruptedException ex) {

    ????????????????????????????? ex.printStackTrace();

    ??????????????????????? }

    ????????????????? }

    ??????????? }

    ????? }

    ????? class Consumer implements Runnable {

    ? ??????????public void run() {

    ????????????????? while(true){

    ??????????????????????? try {

    ????????????????????????????? // 取出一個(gè)對(duì)象,若 basket 為空,等到 basket 有東西為止

    ????????????????????????????? String result = basket.take();

    ??????????????????????? } catch (InterruptedException ex) {

    ????????????????????????????? ex.printStackTrace();

    ??????????????????????? }

    ????????????????? }

    ????? ??????}???????????

    ????? }

    ????? public void execute(){

    ??????????? for(int i=0; i<10; i++){

    ????????????????? new Thread(new Producor()).start();

    ????????????????? new Thread(new Consumer()).start();

    ??????????? }???????????

    ????? }

    ????? public static void main(String[] args){

    ??????????? BlockingQueueTest test = new BlockingQueueTest();

    ??????????? test.execute();

    ????? }?????

    }

    7Atomics 原子級(jí)變量

    原子量級(jí)的變量,主要的類(lèi)有 AtomicBoolean, AtomicInteger, AotmicIntegerArray, AtomicLong, AtomicLongArray, AtomicReference …… 。這些原子量級(jí)的變量主要提供兩個(gè)方法:

    • compareAndSet(expectedValue, newValue): 比較當(dāng)前的值是否等于 expectedValue , 若等于把當(dāng)前值改成 newValue ,并返回 true 。若不等,返回 false 。
    • getAndSet(newValue): 把當(dāng)前值改為 newValue ,并返回改變前的值。

    這些原子級(jí)變量利用了現(xiàn)代處理器( CPU )的硬件支持可把兩步操作合為一步的功能,避免了不必要的鎖定,提高了程序的運(yùn)行效率。

    8Concurrent Collections 共點(diǎn)聚集

    Java 的聚集框架里可以調(diào)用 Collections.synchronizeCollection(aCollection) 將普通聚集改變成同步聚集,使之可用于多線程的環(huán)境下。 但同步聚集在一個(gè)時(shí)刻只允許一個(gè)線程訪問(wèn)它,其它想同時(shí)訪問(wèn)它的線程會(huì)被阻斷,導(dǎo)致程序運(yùn)行效率不高。 Java 5.0 里提供了幾個(gè)共點(diǎn)聚集類(lèi),它們把以前需要幾步才能完成的操作合成一個(gè)原子量級(jí)的操作,這樣就可讓多個(gè)線程同時(shí)對(duì)聚集進(jìn)行操作,避免了鎖定,從而提高了程序的運(yùn)行效率。 Java 5.0 目前提供的共點(diǎn)聚集類(lèi)有: ConcurrentHashMap, ConcurrentLinkedQueue, CopyOnWriteArrayList CopyOnWriteArraySet.

    ?



    Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=846454

    posted on 2006-12-24 22:37 海思 閱讀(356) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): Java小技術(shù)

    評(píng)論

    # re: [轉(zhuǎn)]Java 5.0多線程編程 2006-12-27 07:06 jrobot[匿名]
    非常有用的東東,剛在api里看到這幾個(gè)包想用用呢,非常謝謝分享  回復(fù)  更多評(píng)論
      

    主站蜘蛛池模板: 国产一区二区视频免费| 又大又硬又粗又黄的视频免费看 | 免费亚洲视频在线观看| 亚洲精品国产suv一区88| 最近高清国语中文在线观看免费| 亚洲免费在线视频观看| 91精品成人免费国产片| 亚洲天堂一区二区三区四区| 114级毛片免费观看| 亚洲综合亚洲国产尤物| 青娱乐免费视频在线观看| 亚洲人成在线中文字幕| 国产99视频精品免费观看7| 亚洲男人天堂2018av| 在线免费一区二区| 久久久久亚洲精品无码网址色欲| 国产精品无码素人福利免费| 春暖花开亚洲性无区一区二区| 亚洲成AV人在线观看网址| 一级毛片一级毛片免费毛片| 亚洲精品无码久久久久去q| 国产成人精品无码免费看| 亚洲综合色丁香麻豆| 妻子5免费完整高清电视| 亚洲中文字幕无码av永久| 免费毛片在线视频| 日韩在线视频免费| 亚洲精品无码av人在线观看| 久久这里只精品热免费99| 亚洲精品人成电影网| 成人片黄网站色大片免费| 韩国亚洲伊人久久综合影院| 亚洲日本韩国在线| 很黄很污的网站免费| 亚洲精品综合久久中文字幕| 毛片高清视频在线看免费观看| 精品韩国亚洲av无码不卡区| 国产成人A亚洲精V品无码| 色猫咪免费人成网站在线观看| 亚洲mv国产精品mv日本mv| 国产精品久免费的黄网站|