在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

從多線程設計模式到對 CompletableFuture 的應用

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-06-26 14:18 ? 次閱讀

最近在開發(fā) 延保服務 頻道頁時,為了提高查詢效率,使用到了多線程技術。為了對多線程方案設計有更加充分的了解,在業(yè)余時間讀完了《圖解 Java 多線程設計模式》這本書,覺得收獲良多。本篇文章將介紹其中提到的 Future 模式,以及在實際業(yè)務開發(fā)中對該模式的應用,而這些內容對于本書來說只是冰山一角,還是推薦大家有時間去閱讀原書。

1. Future 模式:“先給您提貨單”

我們先來看一個場景:假如我們去蛋糕店買蛋糕,下單后,店員會遞給我們提貨單并告知“請您傍晚來取蛋糕”。到了傍晚我們拿著提貨單去取蛋糕,店員會先和我們說“您的蛋糕已經(jīng)做好了”,然后將蛋糕拿給我們。

如果將下單蛋糕到取蛋糕的過程抽象成一個方法的話,那么意味著這個方法需要花很長的時間才能獲取執(zhí)行結果,與其一直等待結果,不如先拿著一張“提貨單”,到我們需要取貨的時候,再通過它去取,而獲取“提貨單”的過程是幾乎不耗時的,而這個提貨單對象就被稱為 Future,后續(xù)便可以通過它來獲取方法的返回值。用 Java 來表示這個過程的話,需要使用到 FutureTaskCallable 兩個類,如下:

public class Example {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 預定蛋糕,并定義“提貨單”
        System.out.println("我:預定蛋糕");
        FutureTask future = new FutureTask(() -> {
            System.out.println("店員:請您傍晚來取蛋糕");
            Thread.sleep(2000);
            System.out.println("店員:您的蛋糕已經(jīng)做好了");

            return "Holiland";
        });
        // 開始做蛋糕
        new Thread(future).start();

        // 去做其他事情
        Thread.sleep(1000);
        System.out.println("我:忙碌中...");
        // 取蛋糕
        System.out.println("我:取蛋糕 " + future.get());
    }
}

// 運行結果:
// 我:預定蛋糕
// 店員:請您傍晚來取蛋糕
// 我:忙碌中...
// 店員:您的蛋糕已經(jīng)做好了
// 我:取蛋糕 Holiland

方法的調用者可以將任務交給其他線程去處理,無需阻塞等待方法的執(zhí)行,這樣調用者便可以繼續(xù)執(zhí)行其他任務,并能通過 Future 對象獲取執(zhí)行結果。

它的運行原理如下:創(chuàng)建 FutureTask 實例時,Callable 對象會被傳遞給構造函數(shù),當線程調用 FutureTaskrun 方法時,Callable 對象的 call 方法也會被執(zhí)行。調用 call 方法的線程會同步地獲取結果,并通過 FutureTaskset 方法來記錄結果對象,如果 call 方法執(zhí)行期間發(fā)生了異常,則會調用 setException 方法記錄異常。最后,通過調用 get 方法獲取方法的結果,注意這里可能會拋出方法執(zhí)行時產(chǎn)生的異常

    public void run() {
        // ...
        try {
            // “提貨任務”
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 調用 callable 的 call 方法
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    // 捕獲并設置異常
                    setException(ex);
                }
                if (ran)
                    // 為結果賦值
                    set(result);
            }
        } finally {
            // ...
        }
    }

    protected void set(V v) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            // 將結果賦值給 outcome 全局變量,供 get 時獲取
            outcome = v;
            // 修改狀態(tài)為 NORMAL
            STATE.setRelease(this, NORMAL); // final state
            finishCompletion();
        }
    }

    protected void setException(Throwable t) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            // 將異常賦值給 outcome 變量,供 get 時拋出
            outcome = t;
            // 修改狀態(tài)為 EXCEPTIONAL
            STATE.setRelease(this, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        // 未完成時阻塞等一等
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

    private V report(int s) throws ExecutionException {
        Object x = outcome;
        // 正常結束的話能正常獲取到結果
        if (s == NORMAL)
            return (V)x;
        // 否則會拋出異常,注意如果執(zhí)行中出現(xiàn)異常,調用 get 時會被拋出
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

現(xiàn)在對 Future 模式 已經(jīng)有了基本的了解:它通過 Future 接口來表示未來的結果,實現(xiàn) 調用者與執(zhí)行者之間的解耦提高系統(tǒng)的吞吐量和響應速度,那在實踐中對該模式是如何使用的呢?

2. 對 Future 模式的實踐

因為 延保服務 頻道頁訪問量大且對接口性能要求較高,單線程處理并不能滿足性能要求,所以應用了 Future 模式 來提高查詢效率,但是并沒有借助上文所述的 FutureTask 來實現(xiàn),而是使用了 CompletableFuture 工具類,它們的實現(xiàn)原理基本一致,但是后者提供的方法和對 鏈式編程 的支持使代碼更加簡潔,實現(xiàn)更加容易(相關 API 參考見文末)。

如下是使用 CompletableFuture 異步多線程查詢訂單列表的邏輯,根據(jù)配置的 pageNo 分多條線程查詢各頁的訂單數(shù)據(jù):

        List result = new ArrayList();
        // 并發(fā)查詢訂單列表
        List>> futureList = new ArrayList();
        try {
            // 配置需要查詢的頁數(shù) pageNo,并發(fā)查詢不同頁碼的訂單
            for (int i = 1; i <= pageNo; i++) {
                int curPageNo = i;
                CompletableFuture> future = CompletableFuture.supplyAsync(
                        () -> getOrderInfoList(userNo, curPageNo), threadPoolExecutor);

                futureList.add(future);
            }
            // 等待所有線程處理完畢,并封裝結果值
            for (CompletableFuture> future : futureList) {
                result.addAll(future.get());
            }
        } catch (Exception e) {
            log.error("并發(fā)查詢用戶訂單信息異常", e);
        }

這段代碼中對異常的處理能進行優(yōu)化:第 15 行代碼,如果某條線程查詢訂單列表時發(fā)生異常,那么在調用 get 方法時會拋出該異常,被 catch 后返回空結果,即使有其他線程查詢成功,這些訂單結果值也會被忽略掉,可以針對這一點進行優(yōu)化,如下:

        List result = new ArrayList();
        // 并發(fā)查詢訂單列表
        List>> futureList = new ArrayList();
        try {
            // 配置需要查詢的頁數(shù) pageNo,并發(fā)查詢不同頁碼的訂單
            for (int i = 1; i <= pageNo; i++) {
                int curPageNo = i;
                CompletableFuture> future = CompletableFuture
                        .supplyAsync(() -> getOrderInfoList(userNo, curPageNo), threadPoolExecutor)
                // 添加異常處理
                .exceptionally(e -> {
                    log.error("查詢用戶訂單信息異常", e);
                    return Collections.emptyList();
                });

                futureList.add(future);
            }
            // 等待所有線程處理完畢,并封裝結果值
            for (CompletableFuture> future : futureList) {
                result.addAll(future.get());
            }
        } catch (Exception e) {
            log.error("并發(fā)查詢用戶訂單信息異常", e);
        }

優(yōu)化后針對查詢發(fā)生異常的任務打印異常日志,并返回空集合,這樣即使單線程查詢失敗,也不會影響到其他線程查詢成功的結果。

CompletableFuture 還提供了 allOf 方法,它返回的 CompletableFuture 對象在所有 CompletableFuture 執(zhí)行完成時完成,相比于對每個任務都調用 get 阻塞等待任務完成的實現(xiàn)可讀性更好,改造后代碼如下:

        List result = new ArrayList();
        // 并發(fā)查詢訂單列表
        CompletableFuture>[] futures = new CompletableFuture[pageNo];
        // 配置需要查詢的頁數(shù) pageNo,并發(fā)查詢不同頁碼的訂單
        for (int i = 1; i <= pageNo; i++) {
            int curPageNo = i;
            CompletableFuture> future = CompletableFuture
                    .supplyAsync(() -> getOrderInfoList(userNo, curPageNo), threadPoolExecutor)
                    // 添加異常處理
                    .exceptionally(e -> {
                        log.error("查詢用戶訂單信息異常", e);
                        return Collections.emptyList();
                    });

            futures[i - 1] = future;
        }

        try {
            // 等待所有線程處理完畢
            CompletableFuture.allOf(futures).get();
            for (CompletableFuture> future : futures) {
                List orderInfoList = future.get();
                if (CollectionUtils.isEmpty(orderInfoList)) {
                    result.addAll(orderInfoList);
                }
            }
        } catch (Exception e) {
            log.error("處理用戶訂單結果信息異常", e);
        }

Tips: CompletableFuture 的設計初衷是支持異步編程,所以應盡量避免在CompletableFuture 鏈中使用 get()/join() 方法,因為這些方法會阻塞當前線程直到CompletableFuture 完成,應該在必須使用該結果值時才調用它們。

相關的模式:命令模式

命令模式能將操作的調用者和執(zhí)行者解耦,它能很容易的與 Future 模式 結合,以查詢訂單的任務為例,我們可以將該任務封裝為“命令”對象的形式,執(zhí)行時為每個線程提交一個命令,實現(xiàn)解耦并提高擴展性。在命令模式中,命令對象需要 支持撤銷和重做,那么這便在查詢出現(xiàn)異常時,提供了補償處理的可能,命令模式類圖關系如下:

命令模式.png

3.《圖解Java多線程設計模式》書籍推薦

我覺得本書算得上是一本老書:05 年出版的基于 JDK1.5 的Java多線程書籍,相比于目前我們常用的 JDK1.8 和時髦的 JDK21,在讀之前總會讓人覺得有一種過時的感覺。但是當我讀完時,發(fā)現(xiàn)其中的模式能對應上代碼中的處理邏輯:對 CompletableFuture 的使用正對應了其中的 Future 模式(異步獲取其他線程的執(zhí)行結果)等等,所以我覺得模式的應用不會局限于技術的新老,它是在某種情況下,研發(fā)人員共識或通用的解決方案,在知曉某種模式,采用已有的技術實現(xiàn)它是容易的,而反過來在只掌握技術去探索模式是困難且沒有方向的。

同時,我也在考慮一個問題:對于新人學習多線程技術來說,究竟適不適合直接從模式入門呢?因為我對設計模式有了比較多的實踐經(jīng)驗,所以對“模式”相關的內容足夠敏感,如果新人沒有這些經(jīng)驗的話,這對他們來說會不會更像是一個個知識點的堆砌呢?好在的是,本書除了模式相關的內容,對基礎知識也做足了鋪墊,而且提出的關于多線程編程的思考點也是非常值得參考和學習的,以線程互斥和協(xié)同為例,書中談到:在對線程進行互斥處理時需要考慮 “要保護的東西是什么”,這樣便能夠 清晰的確定鎖的粒度;對于線程的協(xié)同,書中提到的是需要考慮 “放在中間的東西是什么”,直接的拋出這個觀點是不容易理解的,“中間的東西”是在多線程的 生產(chǎn)者和消費者模式 中提出的,部分線程負責生產(chǎn),生產(chǎn)完成后將對象放在“中間”,部分線程負責消費,消費時取的便是“中間”的對象,而合理規(guī)劃這些中間的東西便能 消除生產(chǎn)者和消費者之間的速度差異,提高系統(tǒng)的吞吐量和響應速度。而再深入考慮這兩個角度時,線程的互斥和協(xié)同其實是內外統(tǒng)一的:為了讓線程協(xié)調運行,必須執(zhí)行互斥處理,以防止共享的內容被破壞,而線程的互斥是為了線程的協(xié)調運行才進行的必要操作。


附:CompletableFuture 常用 API

使用 supplyAsync 方法異步執(zhí)行任務,并返回 CompletableFuture 對象

如下代碼所示,調用 CompletableFuture.supplyAsync 靜態(tài)方法異步執(zhí)行查詢邏輯,并返回一個新的 CompletableFuture 對象

CompletableFuture> future = CompletableFuture.supplyAsync(() -> doQuery(), executor);

使用 join 方法阻塞獲取完成結果

如下代碼所示,在封裝結果前,調用 join 方法阻塞等待獲取結果

futureList.forEach(CompletableFuture::join);

它與 get 方法的主要區(qū)別在于,join 方法拋出的是未經(jīng)檢查的異常 CompletionException,并將原始異常作為其原因,這意味著我們可以不需要在方法簽名中聲明它或在調用 join 方法的地方進行異常處理,而 get 方法會拋出 InterruptedExceptionExecutionException 異常,我們必須對它進行處理,get 方法源碼如下:

    public T get() throws InterruptedException, ExecutionException {
        Object r;
        if ((r = result) == null)
            r = waitingGet(true);
        return (T) reportGet(r);
    }

用 thenApply(Function) 和 thenAccept(Consumer) 等回調函數(shù)處理結果

如下是使用 thenApply() 方法對 CompletableFuture 的結果進行轉換的操作:

CompletableFuture future = CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(greeting -> greeting + " World");

使用 exceptionally() 處理 CompletableFuture 中的異常

CompletableFuture 提供了exceptionally() 方法來處理異常,這是一個非常重要的步驟。如果在 CompletableFuture 的運行過程中拋出異常,那么這個異常會被傳遞到最終的結果中。如果沒有適當?shù)漠惓L幚恚敲丛谡{用 get()join() 方法時可能會拋出異常。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    if (true) {
        throw new RuntimeException("Exception occurred");
    }
    return "Hello, World!";
}).exceptionally(e -> "An error occurred");

使用 allOf() 和 anyOf() 處理多個 CompletableFuture

如果有多個 CompletableFuture 需要處理,可以使用 CompletableFuture.allOf() 或者 CompletableFuture.anyOf()allOf() 在所有的 CompletableFuture 完成時完成,而 anyOf() 則會在任意一個 CompletableFuture 完成時完成。

complete()、completeExceptionally()、cancel() 方法

CompletableFuture 的運行是在調用了 complete()completeExceptionally()cancel() 等方法后才會被標記為完成。如果沒有正確地完成 CompletableFuture,那么在調用 get() 方法時可能會永久阻塞。這三個方法在 Java 并發(fā)編程中有著重要的應用。以下是這三個方法的常見使用場景:

complete(T value): 此方法用于顯式地完成一個 CompletableFuture,并設置它的結果值。這在你需要在某個計算完成時,手動設置 CompletableFuture 的結果值的場景中非常有用。例如,你可能在一個異步操作完成時,需要設置 CompletableFuture 的結果值。

CompletableFuture future = new CompletableFuture();
// Some asynchronous operation
future.complete("Operation Result");

completeExceptionally(Throwable ex): 此方法用于顯式地以異常完成一個 CompletableFuture。這在你需要在某個計算失敗時,手動設置 CompletableFuture 的異常的場景中非常有用。例如,你可能在一個異步操作失敗時,需要設置 CompletableFuture 的異常。

CompletableFuture future = new CompletableFuture();
// Some asynchronous operation
future.completeExceptionally(new RuntimeException("Operation Failed"));

cancel(boolean mayInterruptIfRunning): 此方法用于取消與 CompletableFuture 關聯(lián)的計算。這在你需要取消一個長時間運行的或者不再需要的計算的場景中非常有用。例如,你可能在用戶取消操作或者超時的情況下,需要取消 CompletableFuture 的計算。

CompletableFuture future = CompletableFuture.supplyAsync(() -> {
    // Long running operation
});
// Some condition
future.cancel(true);

這些方法都是線程安全的,可以從任何線程中調用。

使用 thenCompose() 處理嵌套的 CompletableFuture

如果在處理 CompletableFuture 的結果時又創(chuàng)建了新的CompletableFuture,那么就會產(chǎn)生嵌套的 CompletableFuture。這時可以使用 thenCompose() 方法來避免 CompletableFuture 的嵌套,如下代碼所示:

CompletableFuture completableFuture
  = CompletableFuture.supplyAsync(() -> "Hello")
    .thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));

使用 thenCombine() 處理兩個 CompletableFuture 的結果

CompletableFuture completableFuture
  = CompletableFuture.supplyAsync(() -> "Hello")
    .thenCombine(CompletableFuture.supplyAsync(() -> " World"), (s1, s2) -> s1 + s2);

審核編輯 黃宇
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關注

    關注

    20

    文章

    2982

    瀏覽量

    106388
  • API
    API
    +關注

    關注

    2

    文章

    1547

    瀏覽量

    63206
  • 多線程
    +關注

    關注

    0

    文章

    279

    瀏覽量

    20195
收藏 人收藏

    評論

    相關推薦

    請問如何在Python中實現(xiàn)多線程與多進程的協(xié)作?

    大家好!我最近在開發(fā)一個Python項目時,需要同時處理多個任務,且每個任務需要不同的計算資源。我想通過多線程和多進程的組合來實現(xiàn)并發(fā),但遇到了一些問題。 具體來說,我有兩個任務,一個是I/O密集型
    發(fā)表于 03-11 06:57

    請問rt-thread studio如何進行多線程編譯?

    ,使用的是5800h+32g內存+sn550 ssd,開啟16線程編譯時cpu的占用率也只能到30%,編譯完整個工程需要3分鐘 感覺多線程編譯設置沒有生效,有辦法提高編譯速度嗎
    發(fā)表于 02-19 08:30

    socket 多線程編程實現(xiàn)方法

    在現(xiàn)代網(wǎng)絡編程中,多線程技術被廣泛應用于提高服務器的并發(fā)處理能力。Socket編程是網(wǎng)絡通信的基礎,而將多線程技術應用于Socket編程,可以顯著提升服務器的性能。 多線程編程的基本概念 多線
    的頭像 發(fā)表于 11-12 14:16 ?724次閱讀

    Python中多線程和多進程的區(qū)別

    Python作為一種高級編程語言,提供了多種并發(fā)編程的方式,其中多線程與多進程是最常見的兩種方式之一。在本文中,我們將探討Python中多線程與多進程的概念、區(qū)別以及如何使用線程池與進程池來提高并發(fā)執(zhí)行效率。
    的頭像 發(fā)表于 10-23 11:48 ?766次閱讀
    Python中<b class='flag-5'>多線程</b>和多進程的區(qū)別

    CPU線程和程序線程的區(qū)別

    CPU的線程與程序的線程在概念、作用、實現(xiàn)方式以及性能影響等方面存在顯著差異。以下是對兩者區(qū)別的詳細闡述,旨在深入探討這一技術話題。
    的頭像 發(fā)表于 09-02 11:18 ?1685次閱讀

    一文掌握Python多線程

    使用線程可以把占據(jù)長時間的程序中的任務放到后臺去處理。
    的頭像 發(fā)表于 08-05 15:46 ?1096次閱讀

    Java CompletableFuture 異步超時實現(xiàn)探索

    用手段便是多線程并行執(zhí)行。這時候就會涉及 CompletableFuture 的使用。 常見使用方式 下面舉例一個常見場景。
    的頭像 發(fā)表于 07-25 14:06 ?522次閱讀

    LWIP多線程強烈建議開啟LWIP_ASSERT_CORE_LOCKED宏,這個在RTT里面要怎么實現(xiàn)?

    LWIP多線程強烈建議開啟LWIP_ASSERT_CORE_LOCKED宏,這個在RTT里面要怎么實現(xiàn),之前參考網(wǎng)上代碼,這樣寫,壓力測試下有概率斷言失敗 extern sys_mutex_t
    發(fā)表于 07-25 06:27

    ESP32會不會有多線程問題,需要加鎖嗎?

    ESP32會不會有多線程問題,需要加鎖嗎
    發(fā)表于 07-19 08:05

    rtt工程移植后線程創(chuàng)建不成功怎么解決?

    之前用l431的板子跑10k的采樣后來要提高100k,更換了f446的芯片,但是根據(jù)新的內存地址0x20000000開始寫入數(shù)據(jù)后,兩個線程創(chuàng)建不成功了,不知道是否是線程的的大小不
    發(fā)表于 07-18 06:44

    使用FX3同步fifo兩地址線能夠配置成四線程模式嗎?

    使用FX3同步fifo兩地址線能夠配置成四線程模式嗎,也就是兩個端點輸出,兩個端點輸入,麻煩大佬回復一下!?
    發(fā)表于 07-02 07:45

    探索虛擬線程:原理與實現(xiàn)

    虛擬線程的引入與優(yōu)勢 在Loom項目之前,Java虛擬機(JVM)中的線程是通過java.lang.Thread類型來實現(xiàn)的,這些線程被稱為平臺線程。 然而,平臺
    的頭像 發(fā)表于 06-24 11:35 ?462次閱讀
    探索虛擬<b class='flag-5'>線程</b>:原理與實現(xiàn)

    動態(tài)線程池思想學習及實踐

    ://www.javadoop.com/post/java-thread-pool? 引言 在后臺項目開發(fā)過程中,我們常常借助線程池來實現(xiàn)多線程任務,以此提升系統(tǒng)的吞吐率和響應性;而線程池的參數(shù)配置
    的頭像 發(fā)表于 06-13 15:43 ?1361次閱讀
    動態(tài)<b class='flag-5'>線程</b>池思想學習及實踐

    bootloader開多線程做引導程序,跳app初始化后直接進hardfualt,為什么?

    如標題,想做一個遠程升級的項目,bootloader引導區(qū)域和app都是開多線程跑的,就是自己寫了個小的任務調度器,沒什么功能主要是想讓程序快速的響應,延時不會對其他程序造成堵塞,程序測試
    發(fā)表于 04-18 06:07

    自主可控關鍵生產(chǎn)環(huán)節(jié)和產(chǎn)能的模擬芯片廠商,IDM模式的全鏈條控制Fabless和Foundry模式的興起

    半導體產(chǎn)業(yè)作為全球科技發(fā)展的核心驅動力,其經(jīng)營模式的演變直接關系到技術創(chuàng)新和產(chǎn)業(yè)升級的步伐。自20世紀80年代以來,半導體行業(yè)的經(jīng)營模式經(jīng)歷了顯著的演變。IDM(集成設備制造商)模式
    的頭像 發(fā)表于 04-17 15:43 ?796次閱讀
    自主可控關鍵生產(chǎn)環(huán)節(jié)和產(chǎn)能的模擬芯片廠商,<b class='flag-5'>從</b>IDM<b class='flag-5'>模式</b>的全鏈條控制<b class='flag-5'>到</b>Fabless和Foundry<b class='flag-5'>模式</b>的興起
    主站蜘蛛池模板: 天天操人人射 | 正在播放国产女免费 | 日韩a级毛片 | 婷婷亚洲综合五月天小说在线 | 四月激情网 | 亚洲第一色在线 | 久久99精品久久久久久久野外 | 久久水蜜桃网 | bt天堂资源种子在线 | 丁香婷婷综合网 | 美女黄页网 | 性欧美视频videos6一9 | 日韩毛片在线 | 六月婷婷激情综合 | 日本三级网站在线线观看 | 亚洲 欧美 自拍 另类 | 天天摸日日摸 | 色妞视频资源在线观看 | 又大又粗又爽黄毛片 | 欧美一级在线观看播放 | 亚洲欧美日韩一区 | 欧美一区亚洲 | 欧美日韩一区不卡 | 久久精品国产乱子伦多人 | 亚洲偷图色综合色就色 | 上课被同桌摸下面做羞羞 | 色天天综合色天天天天看大 | 美日毛片 | 男女视频在线观看免费高清观看 | 天天操天天插天天干 | 天堂资源www天堂在线 | 日本加勒比在线播放 | 久久视频精品36线视频在线观看 | 亚洲国产日韩欧美在线as乱码 | 成年男人永久免费看片 | 奇米社区 | tdg58在线观看 | 国产精品色片 | 国产一区二区三区夜色 | 手机在线黄色网址 | 亚洲天堂亚洲天堂 |