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

硬核詳解 FutureTask 設計與實現

開發(fā)
本文將從實踐和源碼兩個角度分析 FutureTask 的設計與實現思路,希望對你有幫助。

最近看到一篇比較不錯的FutureTask實踐,于是對FutureTask源碼進行了研究,而本文將從實踐和源碼兩個角度分析FutureTask的設計與實現思路,希望對你有幫助。

FutureTask使用示例

我們的批量線程會進行嘗試創(chuàng)建一些任務執(zhí)行,同時我們希望每個任務只有有一個線程去執(zhí)行,其他線程如果拿到這個任務準備執(zhí)行時發(fā)現這個任務已經在執(zhí)行,則等待這個任務的返回結果。

如下圖,假設我們的第一個線程提交task-1至清單成功后,這個線程就會執(zhí)行該任務,而線程2同樣想提交這個任務發(fā)現該任務已存在,則直接等待清單中記錄的這個任務的結果返回。而線程3則也是因為第一次提交任務,所以提交清單成功并執(zhí)行。

我們完全可以通過FutureTask做到這一點,先來說說任務清單,它用于存儲正在執(zhí)行的任務和任務名,為了保證多線程并發(fā)操作安全,筆者直接采用ConcurrentHashMap:

//保存執(zhí)行的任務清單
    private static ConcurrentHashMap<String, Future<String>> taskCache = new ConcurrentHashMap<>();

每一個線程執(zhí)行任務則都是通過executionTask方法,該邏輯一旦檢查任務不存在則創(chuàng)建,然后通過樂觀鎖方式保證任務不存在時才能提交,完成這些操作后通過get等待結果返回。 需要注意的是,這里筆者為了保證邏輯可用做了一點小小的計數處理,每當一個任務通過run執(zhí)行時,筆者會用原子類自增一下,以此判斷多線程執(zhí)行該方法時有沒有重復執(zhí)行任務的情況出現。

privatestatic AtomicInteger counter = new AtomicInteger(0);


public static String executionTask(String taskName) {

       while (true){//執(zhí)行到任務成功
           if (!taskCache.containsKey(taskName)) {//如果任務不存在則創(chuàng)建任務

               Callable<String> callable = () -> taskName;
               FutureTask<String> futureTask = new FutureTask<>(callable);

               //雙重鎖校驗避免任務重復提交
               Future<String> future = taskCache.putIfAbsent(taskName, futureTask);
               if (future == null) {//如果返回空則說明這個任務之前沒有提交過,當前線程直接執(zhí)行
                   futureTask.run();
                   //累加執(zhí)行的任務數
                   counter.incrementAndGet();
               }
           }

           //任務非空或提交任務完成的線程在這里等待任務返回
           try {
               return taskCache.get(taskName).get();
           } catch (Exception e) {
               //如果保存則從清單中移除
               taskCache.remove(taskName);
           }
       }

    }

對應的測試代碼如下,筆者提交10w個線程執(zhí)行10個任務,從計數器的輸出結果來看,執(zhí)行了10個任務,沒有問題:

public static void main(String[] args) throws Exception {
        //創(chuàng)建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(10_000);



        for (int i = 0; i < 10_000; i++) {
            //通過取模運算保證10w個線程只會創(chuàng)建 名為10以內的任務
            int finalI = i % 10;
            //提交任務
            executorService.submit(() -> FutureTaskExample.executionTask(String.valueOf(finalI)));

        }

        //等待結束
        executorService.shutdown();
        while (!executorService.isTerminated()) {

        }
        //查看計數
        System.out.println(counter.get());//10

    }

FutureTask在閉鎖下的哲學

本質上FutureTask也可以是一種閉鎖,即在FutureTask對應線程未完成運算前,FutureTask這個閉鎖就像一個大門一樣不允許所有線程通過,只有FutureTask完成運算進入完成狀態(tài)后,其它線程才能通過。

例如我們希望通過FutureTask執(zhí)行一些耗時的運算,此時就可以:

  • 通過FutureTask提交任務
  • 異步任務運算期間,執(zhí)行一些其它任務
  • 通過get阻塞等待FutureTask結果返回
  • FutureTask任務返回線程通過,打印輸出結果

所以FutureTask也是一個高效的異步工具,我們可以將一些耗時的操作提前啟動,著手其它耗時操作等待完成后拿結果:

對應的我們也給出上述實現的代碼示例:

public class Task {
    //休眠完成后,返回一個隨機數
    privatefinal FutureTask<Integer> futureTask = new FutureTask<>(() -> {
        ThreadUtil.sleep(1000);
        return RandomUtil.randomInt();
    });

    privatefinal Thread thread = new Thread(futureTask);



    //啟動任務執(zhí)行
    public void start() {
        thread.start();
    }


    //阻塞獲取結果
    public int get() throws ExecutionException, InterruptedException {
        return futureTask.get();
    }

}

使用實例如下,即通過FutureTask執(zhí)行異步運算后,通過get執(zhí)行閉鎖控制異步流程:

Task task = new Task();

        long begin = System.currentTimeMillis();
        task.start();

        //futureTask異步運行期間,執(zhí)行一些業(yè)務邏輯
        System.out.println("do something...");

        //阻塞等待futureTask完成,即利用get方法實現一個閉鎖的操作
        int result = task.get();
        long end = System.currentTimeMillis();
        Console.log("result: {},cost:{}ms", result, end - begin);

輸出結果如下,可以看到整體來說利用了異步運算期間執(zhí)行一些其它操作,同時還使用get保證整體流程順序正常:

do something...
result: -1158881871,cost:1009ms

FutureTask狀態(tài)機的扭轉

在正式進行源碼介紹之前,筆者先簡單介紹一下FutureTask執(zhí)行狀態(tài)的扭轉,FutureTask在創(chuàng)建時是全新的任務,此時它的狀態(tài)就是NEW,一旦調用run之后就會開始運行,此時就會出現4個分支:

  • 當任務正確執(zhí)行完成之后,先將狀態(tài)設置為完成中COMPLETING,然后將執(zhí)行結果存入outcome變量中,完成后再將狀態(tài)設置為正常結束,即NORMAL。
  • 一旦任務執(zhí)行出現異常,FutureTask則同樣將任務設置為完成中,將結果設置為null之后,調整狀態(tài)為執(zhí)行出錯EXCEPTIONAL。
  • 當任務在執(zhí)行過程中,需要進行打斷操作時,FutureTask會將狀態(tài)設置為打斷中INTERRUPTING,一旦線程正常被打斷,任務就會被設置為終態(tài)INTERRUPTED。
  • FutureTask同樣支持不打斷當前執(zhí)行線程,這一點筆者會在后文中說明,這種情況則直接將線程設置為CANCELLED。

一旦狀態(tài)按照預期調整為終態(tài)后,FutureTask就會喚醒那些等待任務執(zhí)行完成的線程:

這4種狀態(tài)扭轉,我們也可以通過閱讀FutureTask上關于狀態(tài)變量的注釋了解:

/*
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    privatevolatileint state;
    privatestaticfinalint NEW          = 0;
    privatestaticfinalint COMPLETING   = 1;
    privatestaticfinalint NORMAL       = 2;
    privatestaticfinalint EXCEPTIONAL  = 3;
    privatestaticfinalint CANCELLED    = 4;
    privatestaticfinalint INTERRUPTING = 5;
    privatestaticfinalint INTERRUPTED  = 6;

FutureTask如何執(zhí)行

FutureTask在創(chuàng)建之初時的初始態(tài)為NEW,也就是整數變量0,一旦某個線程執(zhí)行這個任務,JDK8版本會通過原子操作將記錄運行線程的地址(這個位置的偏移量用runnerOffset記錄)設置為當前線程,一旦操作成功則執(zhí)行該task封裝的Callable任務,如果運行成功則將result設置為運行后的結果,并將ran標志為true,并將任務狀態(tài)設置為NORMAL:

反之如果運行失敗,則將ran設置為false,result設置為空,并將錯誤信息設置到stateOffset標志位上了,將任務運行狀態(tài)設置為終態(tài)EXCEPTIONAL,然后喚醒其他需要處理這個任務的線程:

對此我們給出FutureTask的底層實現,可以看到FutureTask只有狀態(tài)為NEW且通過CAS操作將runner設置為自己的線程才能執(zhí)行任務,而后續(xù)的線程如果看到state不為new則只能獲取結果,而不能執(zhí)行,這就FutureTask避免重復運行的核心設計所在。 進行樂觀鎖上鎖拿到執(zhí)行權之后,就會基于CAS上鎖的線程進行任務調用,對應的結果扭轉就如上文所說,這里讀者可以參考筆者注釋自行閱讀:

public void run() {
//1. 為NEW 說明是第一次運行,則可以通過CAS操作獲取執(zhí)行權
//2. 不為NEW,直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            //如果任務不為空且狀態(tài)為NEW則調用call運行
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                 //運行成功則記錄結果到result 并將ran 設置為true
                    result = c.call();    
                    ran = true;
                } catch (Throwable ex) {
                //如果報錯則result設置為空,并將ran設置為false
                    result = null;
                    ran = false;
                    //并將任務狀態(tài)設置為錯誤
                    setException(ex);
                }
                //如果運行成功則將結果存到outcome中
                if (ran)
                    set(result);
            }
        } finally {
            //......
        }
    }

這里我們直接查看獲取結果成功后的狀態(tài)設置方法set方法,可以看到其內部先通過cas將status即狀態(tài)字段設置為COMPLETING,完成結果設置之后,再通過putOrderedInt將狀態(tài)設置為終態(tài)NORMAL,這么做的原因是為什么呢?

protected void set(V v) {
  //先CAS設置為完成中
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //設置結果完成后,才能設置狀態(tài)為正常結束
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

多線程執(zhí)行FutureTask情況下,大部分判斷都需要依賴于COMPLETING這個中間態(tài)(利用get和COMPLETING方法),所以這個狀態(tài)的可見性要求相對高一些,所以在進行結果設置之前,先通過CAS的方式進行更新status字段狀態(tài),這種操作是需要將storeLoad屏障,雖然性能表現差一些,但可以保證可見性和有序性,所以先通過這個操作保證其他線程對于這個中態(tài)狀態(tài)可見保證并發(fā)操作一致性。

然后完成任務處理結果設置,此時在邏輯上我們可以認定這個任務是處理完成了,因為大部分的邏輯判斷都是依賴于COMPLETING,對于終態(tài)(NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTED)的可見性要求不高,所以FutureTask的設計者直接采用putOrderedInt這種操作保證寫入不會被重排序,但不會立即刷到一致性內存行上,所以在性能表現上會出色一些。

上述對于正確處理結果的設置,可以在set這個方法的源碼上得以印證,讀者可以結合上文筆者所說和注釋自行了解這段邏輯:

protected void set(V v) {
 //通過cas操作volatile變量status完成中態(tài)設置,又保證的多核心可見性
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //設置處理結果到outcome上,后續(xù)其他線程get都是從這個outcome變量上獲取
            outcome = v;
            //因為終態(tài)可見性要求不高,所以通過putOrderedInt設置終態(tài),保證寫入有序性
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            //喚醒其他線程處理當前任務
            finishCompletion();
        }
    }

多線程如何獲取FutureTask執(zhí)行結果

其他線程需要從get方法獲取結果時,其內部本質就是調用awaitDone等待完成,假設我們用putOrderedInt寫入,且狀態(tài)對于當前線程不可見,那么這個線程要做的也僅僅是yield讓出處理器的使用權,相比之下volatile寫這種需要增加內存屏障寫入的開銷,這種內存消耗無論從概率還是消耗上,都是劃得來的。

最后完成上述操作,通過finishCompletion通知其他的等待線程可以開始處理FutureTask了。

對此我們也給出get的源碼和注釋,讀者可結合上文感知一下邏輯:

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        //狀態(tài)在COMPLETING及其之前調用awaitDone等待完成
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        finallong deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
          //......
          //如果狀態(tài)處于中態(tài)完成中,則讓出線程對于處理器的使用權
            elseif (s == COMPLETING) // cannot time out yet
                Thread.yield();
//......
}

我們給出狀態(tài)設置為終態(tài)之后,finishCompletion的邏輯,比較簡單,可以看到它僅是獲取當前等待節(jié)點的后一個線程的WaitNode ,通過unpark將其喚醒,然后獲取其后繼節(jié)點繼續(xù)進行喚醒操作:

private void finishCompletion() {
        // assert state > COMPLETING;
        //獲取后繼節(jié)點
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                //將該等待節(jié)點直接喚醒
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    //指向已喚醒節(jié)點的后繼節(jié)點
                    WaitNode next = q.next;
                    //若為空直接退出
                    if (next == null)
                        break;
                    //將后繼指針設置為空,輔助gc,并讓q指向后續(xù)節(jié)點,繼續(xù)進行喚醒操作    
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

      //......
    }

FutureTask的異常處理

有了上文設置成功的邏輯解說,相信讀者對于setException的邏輯處理也就比較熟悉了,這里筆者就不再展開,讀者可自行查看代碼和注釋:

protected void setException(Throwable t) {
    //原子操作設置為完成中,保證可見性
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
         //結果設置為null
            outcome = t;
            //通過高性能有序寫putOrderedInt設置終態(tài)為錯誤態(tài)
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            //喚醒其他線程
            finishCompletion();
        }
    }

FutureTask如何取消

任務取消操作有兩種情況,如果我們傳參為true,則會通過原子操作將狀態(tài)設置為打斷中,再嘗試打斷正在執(zhí)行的線程,然后將狀態(tài)設置為已打斷這個終態(tài)INTERRUPTED,再喚醒其他線程。

若傳參設置為false,則直接原子操作設置為已取消,然后直接喚醒其他線程。

public boolean cancel(boolean mayInterruptIfRunning) {
//如果狀態(tài)不為new則進行狀態(tài)原子設置操作
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            returnfalse;
        try {    
         //如果入參為true則嘗試打斷線程
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                 //完成打斷設置狀態(tài)為INTERRUPTED
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
        //喚醒其他線程
            finishCompletion();
        }
        returntrue;
    }

FutureTask如何避免任務重復執(zhí)行

我們假設當前任務為初始化NEW,這意味所有任務都可以嘗試進行上樂觀鎖獲取執(zhí)行勸,如下所示只有設置成功的線程才可以進入后續(xù)的執(zhí)行,而其他線程則直接返回:

public void run() {
  //只有狀態(tài)為NEW的情況下,當前執(zhí)行的線程才會通過CAS獲取樂觀鎖,之后獲取樂觀鎖成功的才能執(zhí)行任務,其他的線程會直接返回
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
  //......
  //執(zhí)行任務邏輯
}

JDK8版本FutureTask的變化

這一點,筆者參閱了1.6版本的FutureTask實現,其內部是通過AQS來維護需要執(zhí)行任務的線程及其狀態(tài),而JDK8版本則專門為FutureTask創(chuàng)建了一個state字段,多線程之間通過CAS操作進行維護。

我們先來說明一下早期版本的設計再說明原因,在較早的版本FutureTask內部線程競爭關系和任務狀態(tài)都采用AQS進行維護,假設當前任務被取消,則執(zhí)行這個操作線程會通過原子操作將AQS隊列的state字段更新為CANCELLED。

同理執(zhí)行任務時,也是先通過AQS的原子操作將狀態(tài)設置為RUNNING,執(zhí)行完成之后將操作結果原子修改為RAN,并將結果記錄到FutureTask的reuslt中:

對此我們給出老版本的FutureTask的run方法,邏輯如上文所說,筆者這里就不多做贅述:

public void run() {
        sync.innerRun();
    }

void innerRun() {
   //原子操作將狀態(tài)設置為RUNNING
            if (!compareAndSetState(0, RUNNING))
                return;
            try {
             //獲取當前線程,檢查當前狀態(tài)還是RUNNING則用innerSet當前執(zhí)行執(zhí)行callable,然后將結果設置到result中,并將狀態(tài)修改為RAN
                runner = Thread.currentThread();
                if (getState() == RUNNING) // recheck after setting thread
                    innerSet(callable.call());
                else
                    releaseShared(0); // cancel
            } catch (Throwable ex) {
             //......
            }
        }

我們來看看cancel方法,可以看到只要不是RAN這種終態(tài),就可以嘗試打斷線程:

public boolean cancel(boolean mayInterruptIfRunning) {
        return sync.innerCancel(mayInterruptIfRunning);
    }

boolean innerCancel(boolean mayInterruptIfRunning) {
            for (; ; ) {
             //獲取狀
                int s = getState();
                //如果是RAN或者canceled則直接返回
                if (ranOrCancelled(s))
                    returnfalse;
                  //將狀態(tài)設置為CANCELLED
                if (compareAndSetState(s, CANCELLED))
                    break;
            }
            // 按照mayInterruptIfRunning的布爾值決定是否打斷線程
            if (mayInterruptIfRunning) {
                Thread r = runner;
                if (r != null)
                    r.interrupt();
            }
            releaseShared(0);
            done();
            returntrue;
        }

有意思的來了,我們試想這樣一個場景:

  • 假設我們執(zhí)行上述run方法的任務處于RUNNING狀態(tài),此時當前線程1就可以調用interrupt打斷這個線程
  • 此時線程2看到當前任務處于Running(源碼說明不是ran或者canceled即可打斷)直接將其打斷
  • 又假設我們這個線程跑去執(zhí)行別的task任務

所以線程1就可能在執(zhí)行別的任務期間被打斷,進而出現幻覺死:

于是就有了JDK7之后版本,FutureTask專門使用一個volatile的state維護任務的狀態(tài),并且在打斷前設置一個中態(tài)INTERRUPTING 即打斷中,執(zhí)行任務的線程1在運行完成將結果存入outcome之后,看到打斷中這個中態(tài)就會循環(huán)調用yield讓出執(zhí)行權,直到執(zhí)行cancel操作的線程完成,由此保證了打斷操作永遠被限制在當前FutureTask生命周期以內。

對應我們給出cancel操作的源碼,可以看到它是先設置為打斷中INTERRUPTING 然后在進行打斷再設置打斷完成INTERRUPTED的終態(tài):

public boolean cancel(boolean mayInterruptIfRunning) {
//原子操作設置狀態(tài)為打斷中
        if (!(state == NEW &&
              UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            returnfalse;
        try { 
         //嘗試打斷線程,完成后設置為INTERRUPTED
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        returntrue;
    }

假設上一步的線程的打斷操作還未完成,這里可以直接理解為執(zhí)行interrupt打斷之前的代碼段,而執(zhí)行我們的run的線程已經運行完成并將結果設置到outcome時,如下所示會執(zhí)行finally 語句塊的handlePossibleCancellationInterrupt,它在看到打斷中的操作后會循環(huán)調用Thread.yield()讓當前線程讓出CPU執(zhí)行權,直到其他線程的cancel操作完成:

public void run() {
        //.....
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                 //執(zhí)行邏輯運算
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                   //......
                }
                //將結果設置到outcome中
                if (ran)
                    set(result);
            }
        } finally {
          //......
          //讀取到打斷中的狀態(tài),調用handlePossibleCancellationInterrupt讓線程yield出去,保證打斷操作限定在當前線程內
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }


private void handlePossibleCancellationInterrupt(int s) {
       //循環(huán)yield直到cacnel操作完成
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt
    }

關于這個問題我們也可以參考源碼上的注釋:

/*
     * Revision notes: This differs from previous versions of this
     * class that relied on AbstractQueuedSynchronizer, mainly to
     * avoid surprising users about retaining interrupt status during
     * cancellation races. Sync control in the current design relies
     * on a "state" field updated via CAS to track completion, along
     * with a simple Treiber stack to hold waiting threads.
     *
     * Style note: As usual, we bypass overhead of using
     * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
     */
責任編輯:趙寧寧 來源: 寫代碼的SharkChili
相關推薦

2023-10-11 18:35:20

Java編程語言

2020-11-03 09:10:18

JUC-Future

2011-10-10 09:35:54

云計算服務器

2022-11-15 09:57:51

Java接口

2009-07-10 16:14:29

MVC設計模式Swing

2022-06-02 11:12:10

CallableFuture

2024-11-22 15:00:00

開源Redis鏈表

2010-06-11 14:55:20

2023-10-17 15:56:37

FutureTask線程

2011-08-29 15:30:53

JavaMELua

2009-03-03 09:13:36

工作流BPM業(yè)務流程

2020-12-28 07:33:21

SkipListJava跳表

2015-06-30 11:05:11

flexibleWebAPP設計

2015-11-03 09:28:52

Hybrid技術設計實現

2023-05-26 08:24:17

短信渠道模型

2023-08-10 10:13:35

轉轉短鏈平臺

2022-09-12 07:17:20

redis命令redissynce

2022-09-14 09:37:22

數據系統(tǒng)

2020-10-23 08:31:15

Nodejs-Ipc設計實現
點贊
收藏

51CTO技術棧公眾號

亚洲va欧美va| 激情综合丁香五月| 国产经典三级在线| 91婷婷韩国欧美一区二区| 日本精品一区二区三区在线播放视频| 欧美做受高潮6| av日韩一区| 午夜影院久久久| 午夜精品短视频| 国产成人a人亚洲精品无码| 亚洲精品一级| 日韩在线观看高清| 中文字幕在线播放视频| 日本免费在线一区| 欧美日韩亚洲一区二区| 亚洲国产精品影视| 蜜桃视频在线观看网站| 国产精品亚洲第一| 国产精品久久久久久久久借妻 | 国产成人麻豆免费观看| 欧美精品97| 一区二区三区在线播放欧美| av漫画在线观看| 国产资源一区| 欧美性猛交xxxx乱大交蜜桃| www.男人天堂网| av网站大全在线观看| av电影天堂一区二区在线观看| 91九色国产视频| 国产伦精品一区二区三区视频网站| 你懂的成人av| 精品国产欧美一区二区三区成人| 亚洲最大免费视频| 成人福利一区| 日韩欧美激情在线| 日本高清久久久| 国产一区二区精品调教| 日韩欧美亚洲综合| av免费观看大全| 久久青青色综合| 亚洲少妇中出一区| 亚洲综合视频一区| 成人全视频高清免费观看| 久久网站最新地址| 欧美成熟毛茸茸复古| 狠狠躁日日躁夜夜躁av| 国产成人免费网站| 亚洲影影院av| 国产欧美综合视频| 国产美女娇喘av呻吟久久| 成人久久久久久久| 国产剧情久久久| 国产在线观看免费一区| 成人黄在线观看| 国产又黄又粗又硬| 国内精品免费在线观看| 91在线看www| 国产农村妇女毛片精品久久| 韩国成人福利片在线播放| 国产日韩在线观看av| 伊人影院中文字幕| 激情深爱一区二区| 亚洲qvod图片区电影| 国产成人三级一区二区在线观看一 | 国产一区二区精品丝袜| 神马久久久久久久久久久| 精品国产一区二区三区小蝌蚪| 亚洲香蕉成视频在线观看| 亚洲精品视频网址| 国产精品精品国产一区二区| 久久99视频精品| 天天操天天射天天爽| 国产欧美91| 国产精品白嫩美女在线观看| 一区两区小视频| 国产精品一色哟哟哟| 亚洲综合av影视| 手机在线精品视频| 久久亚洲综合色一区二区三区| 品久久久久久久久久96高清| 日本视频在线免费观看| 一区二区三区四区不卡在线 | 久久99精品视频| 亚洲自拍另类欧美丝袜| 天天爽夜夜爽夜夜爽| 久久精品日产第一区二区三区高清版| 亚洲a∨一区二区三区| 福利成人在线观看| 亚洲女与黑人做爰| 国产性xxxx18免费观看视频| 粉嫩av一区二区三区四区五区| 91精品婷婷国产综合久久性色| 亚洲成人精品在线播放| 欧美日韩大片免费观看| 精品国内亚洲在观看18黄| 国产在线观看免费av| 日韩av在线播放中文字幕| 91九色极品视频| 激情小视频在线| 一区二区三区久久久| 欧美日韩亚洲一二三| 精品一区二区三区中文字幕| 亚洲视频axxx| 国产一级性生活| 久久精品国产亚洲a| 粉嫩av免费一区二区三区| 国产51人人成人人人人爽色哟哟| 亚洲精品视频在线| 亚洲综合在线网站| 91夜夜蜜桃臀一区二区三区| 在线播放精品一区二区三区 | 国产高清一区二区三区视频| 欧美日韩在线免费观看| 国产永久免费网站| 国产成人1区| 欧美交受高潮1| 一级黄色大毛片| 久久综合一区二区| 99热久久这里只有精品| 日本中文字幕视频一区| 亚洲欧洲av一区二区三区久久| 免费成人在线电影| 亚洲国产导航| 国产精品久久久久久久午夜| 中文字幕乱码人妻无码久久| 国产美女撒尿一区二区| 中文字幕日韩在线观看| 97免费在线观看视频| 国产成人精品影院| 一级一片免费播放| 欧美日韩免费一区二区三区| 成人18精品视频| 国产成人自拍高清视频在线免费播放| 美国毛片一区二区三区| 91av网站在线播放| 理论片中文字幕| 亚洲视频1区2区| 亚洲欧美日韩三级| 日韩在线精品| 国产精品专区一| av在线播放免费| 国产欧美日韩在线一区二区| 日韩精品在线观看网站| 亚洲国产精品成人无久久精品| 国产精品一级在线| 一道本在线观看视频| 国产精品一区二区免费视频| 国产午夜精品久久久久| 911国产在线| 国模大尺度视频| 蜜臀av一区二区三区有限公司| 高清精品视频| 久久久久久久久网站| 依人在线免费视频| 91九色成人| 久久精品国产一区二区三区| 在线观看国产成人| 中文字幕中文字幕中文字幕亚洲无线| 国产超碰在线播放| 日韩片欧美片| 91丝袜美腿美女视频网站| av毛片在线| 精品欧美久久久| 日本一本高清视频| 2023国产精品| 五月婷婷激情久久| 欧美一级本道电影免费专区| 奇米一区二区三区四区久久| 第一视频专区在线| 欧美日韩视频不卡| 亚洲色图综合区| 成人免费精品视频| 久久精品.com| 久久日文中文字幕乱码| 亚洲xxxx在线| 成人黄色动漫| 亚洲色图第三页| 国产一区二区在线视频聊天| 亚洲自拍偷拍欧美| 亚洲第一页av| 精品一区中文字幕| 国产精品69久久久| 精品国产91乱码一区二区三区四区 | 欧美性生给视频| 蜜臀av性久久久久av蜜臀妖精| 一本久久a久久精品vr综合 | 欧美三级韩国三级日本一级| 国产在线观看精品一区二区三区| 日韩综合第一页| 一区二区三区成人精品| 日韩电影天堂视频一区二区| 9999在线精品视频| 91成人在线播放| 欧美高清视频| 亚洲精品国产欧美| 在线免费观看中文字幕| 亚洲福中文字幕伊人影院| 性欧美一区二区| 成人亚洲一区二区一| 美女黄色片视频| 欧美日韩天堂| 日韩av大全| 国产精品国产| 国产欧美日韩中文| 色在线视频观看| 欧美乱大交做爰xxxⅹ性3| 久久视频www| 日韩欧美美女一区二区三区| 亚洲欧美日韩一区二区三区四区| 亚洲精品中文在线影院| 成人国产精品久久久网站| 国产99久久久国产精品| 天天干天天草天天| 亚洲免费综合| 蜜桃视频一区二区在线观看| 精品成av人一区二区三区| 国产视频一区二区三区四区| 亚洲我射av| 国产精品久久久久久久久久久久| 欧美aaa免费| 久久天堂电影网| 91精品国产91久久久久游泳池 | 欧美日韩免费在线观看| 欧洲猛交xxxx乱大交3| 中文字幕欧美国产| 91网站免费入口| 91影院在线免费观看| 亚洲黄色小说在线观看| 国产一区二区成人久久免费影院| 国产福利影院在线观看| 丝瓜av网站精品一区二区| 激情五月宗合网| 91久久久久| 成人午夜视频在线观看免费| 韩国在线一区| 毛片在线视频观看| 欧美影视一区| 特级西西444| 伊人青青综合网| 国产精品12p| 日韩欧美综合| 天天爱天天做天天操| 66久久国产| 91麻豆天美传媒在线| 午夜精品毛片| 天天综合五月天| 欧美激情无毛| 老子影院午夜伦不卡大全| 欧美午夜影院| 国产极品尤物在线| 国产精品嫩草99av在线| 亚欧无线一线二线三线区别| 亚洲美女一区| 免费无码av片在线观看| 日日摸夜夜添夜夜添精品视频 | 国产精品一区二区在线播放 | av午夜精品一区二区三区| 男女性杂交内射妇女bbwxz| 成人污视频在线观看| 欧美日韩人妻精品一区在线| 不卡av在线免费观看| 北岛玲一区二区| 久久精品一区二区| 欧美精品日韩在线| 亚洲人吸女人奶水| 国产亚洲精品女人久久久久久| 亚洲一区二区三区三| 四虎精品永久在线| 欧美性大战久久| 国产免费高清av| 8v天堂国产在线一区二区| 亚洲一区制服诱惑| 日本一区二区三区在线观看视频| 亚洲精品国精品久久99热一| 深夜福利免费在线观看| 亚洲精选中文字幕| 超碰国产在线| 九色91av视频| 国产免费不卡| 国产视频999| 欧美经典一区| 玖玖玖精品中文字幕| 四季av一区二区三区免费观看| 妞干网这里只有精品| 亚洲茄子视频| 天天色综合社区| 成人一级黄色片| av在线播放中文字幕| 亚洲一区二区影院| 一级久久久久久| 精品久久一区二区三区| 免费播放片a高清在线观看| 久久精品中文字幕一区| 九色porny自拍视频在线观看| 国产精品久久久久91| 视频免费一区二区| 日韩av一级大片| 国自产拍偷拍福利精品免费一| 97视频在线免费播放| 国产伦精品一区二区三区在线观看| 亚洲中文字幕一区| 日韩毛片高清在线播放| 国产精品suv一区二区三区| 欧美精三区欧美精三区| 涩涩视频在线观看免费| 久久这里有精品| 欧美aaa视频| 国产一区二区高清不卡| 久久精品99久久无色码中文字幕| 亚洲mm色国产网站| 在线观看日韩精品视频| 老司机2019福利精品视频导航| 国产日韩欧美a| 国产有码一区二区| 国产黄色小视频网站| av在线播放一区二区三区| 青青青视频在线免费观看| 亚洲成人动漫在线观看| 国产精选久久久| 中国人与牲禽动交精品| 人在线成免费视频| 波多野结衣精品久久| 99成人超碰| 美女网站免费观看视频| 99久久99久久免费精品蜜臀| 欧美成人精品欧美一| 免费不卡在线观看| 欧美重口另类videos人妖| 日韩精品免费观看视频| 九九99久久| 国模一区二区三区| 国产资源中文字幕| 国产精品久久久久一区 | 99久久久久| 9久久婷婷国产综合精品性色| 26uuu精品一区二区三区四区在线| 久久国产在线视频| 日韩女优视频免费观看| 成a人片在线观看| 成人春色激情网| 福利一区二区三区四区| 亚洲色图丝袜| 日韩一区二区三区视频在线观看| 日韩中文在线字幕| www国产在线观看 | 在线精品视频免费观看| 亚洲欧洲久久久| 精品福利免费观看| 蜜桃91麻豆精品一二三区| 欧美情侣性视频| 亚洲1区在线| 美女扒开大腿让男人桶| 粉嫩av亚洲一区二区图片| 国产真实乱偷精品视频| 亚洲国产精品成人av| 在线天堂资源www在线污| 开心色怡人综合网站| 三级影片在线观看欧美日韩一区二区| www.88av| 欧洲日韩一区二区三区| av播放在线| 91丨九色丨国产在线| 欧美色综合网| 中文字幕精品久久久| 一本色道亚洲精品aⅴ| 高清在线观看av| 91老司机在线| 黄色亚洲在线| 四虎永久免费影院| 欧美性色aⅴ视频一区日韩精品| 日本三级视频在线观看| 亚洲综合日韩在线| 亚洲人成人一区二区三区| 人妻少妇一区二区| 欧美色图免费看| 色爱综合区网| 欧美精品一区在线| 麻豆成人av在线| 久久国产在线视频| 精品视频久久久久久久| 992tv国产精品成人影院| 欧洲精品视频在线| 91在线视频在线| 一区二区不卡视频在线观看| 欧美极品第一页| 欧美日韩一二| 性生活在线视频| 欧美性猛交xxxx| 国产黄a三级三级三级av在线看| 精品国产乱码久久久久久蜜柚| 奇米色777欧美一区二区| 免费中文字幕在线| 亚洲人成网站999久久久综合| japansex久久高清精品| 一女被多男玩喷潮视频| 亚洲欧洲日韩在线| 男人的天堂在线| 91原创国产| 久久超碰97中文字幕| 国产福利拍拍拍|