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

10問10答:你真的了解線程池嗎?

開發 后端
《Java開發手冊》中強調,線程資源必須通過線程池提供,而創建線程池必須使用ThreadPoolExecutor。手冊主要強調利用線程池避免兩個問題,一是線程過渡切換,二是避免請求過多時造成OOM。但是如果參數配置錯誤,還是會引發上面的兩個問題。所以本節我們主要是討論ThreadPoolExecutor的一些技術細節,并且給出幾個常用的最佳實踐建議。

 [[403505]]

《Java開發手冊》中強調,線程資源必須通過線程池提供,而創建線程池必須使用ThreadPoolExecutor。手冊主要強調利用線程池避免兩個問題,一是線程過渡切換,二是避免請求過多時造成OOM。但是如果參數配置錯誤,還是會引發上面的兩個問題。所以本節我們主要是討論ThreadPoolExecutor的一些技術細節,并且給出幾個常用的最佳實踐建議。

我在查找資料的過程中,發現有些問題存在爭議。后面發現,一部分原因是因為不同JDK版本的現實是有差異的。因此,下面的分析是基于當下最常用的版本JDK1.8,并且對于存在爭議的問題,我們分析源碼,源碼才是最準確的。

1 corePoolSize=0會怎么樣

這是一個爭議點。我發現大部分博文,不論是國內的還是國外的,都是這樣回答這個問題的:

提交任務后,先判斷當前池中線程數是否小于corePoolSize,如果小于,則創建新線程執行這個任務。
否者,判斷等待隊列是否已滿,如果沒有滿,則添加到等待隊列。
否者,判斷當前池中線程數是否大于maximumPoolSize,如果大于則拒絕。
否者,創建一個新的線程執行這個任務。
按照上面的描述,如果corePoolSize=0,則會判斷等待隊列的容量,如果還有容量,則排隊,并且不會創建新的線程。

—— 但其實,這是老版本的實現方式,從1.6之后,實現方式就變了。我們直接看execute的源碼(submit也依賴它),我備注出了關鍵一行:

  1. int c = ctl.get();        if (workerCountOf(c) < corePoolSize) {            if (addWorker(command, true))                return;            c = ctl.get();        }        if (isRunning(c) && workQueue.offer(command)) {            int recheck = ctl.get();            if (! isRunning(recheck) && remove(command))                reject(command);            // 注意這一行代碼,添加到等待隊列成功后,判斷當前池內線程數是否為0,如果是則創建一個firstTask為null的worker,這個worker會從等待隊列中獲取任務并執行。            else if (workerCountOf(recheck) == 0)                addWorker(null, false);        }        else if (!addWorker(command, false))            reject(command); 

線程池提交任務后,首先判斷當前池中線程數是否小于corePoolSize。
如果小于則嘗試創建新的線程執行該任務;否則嘗試添加到等待隊列。
如果添加隊列成功,判斷當前池內線程數是否為0,如果是則創建一個firstTask為null的worker,這個worker會從等待隊列中獲取任務并執行。
如果添加到等待隊列失敗,一般是隊列已滿,才會再嘗試創建新的線程。
但在創建之前需要與maximumPoolSize比較,如果小于則創建成功。
否則執行拒絕策略。

上述問題需區分JDK版本。在1.6版本之后,如果corePoolSize=0,提交任務時如果線程池為空,則會立即創建一個線程來執行任務(先排隊再獲取);如果提交任務的時候,線程池不為空,則先在等待隊列中排隊,只有隊列滿了才會創建新線程。

所以,優化在于,在隊列沒有滿的這段時間內,會有一個線程在消費提交的任務;1.6之前的實現是,必須等隊列滿了之后,才開始消費。

2 線程池創建之后,會立即創建核心線程么

之前有人問過我這個問題,因為他發現應用中有些Bean創建了線程池,但是這個Bean一般情況下用不到,所以咨詢我是否需要把這個線程池注釋掉,以減少應用運行時的線程數(該應用運行時線程過多。)

不會。從上面的源碼可以看出,在剛剛創建ThreadPoolExecutor的時候,線程并不會立即啟動,而是要等到有任務提交時才會啟動,除非調用了prestartCoreThread/prestartAllCoreThreads事先啟動核心線程。

prestartCoreThread:Starts a core thread, causing it to idly wait for work. This overrides the default policy of starting core threads only when new tasks are executed.
prestartAllCoreThreads:Starts all core threads.

3 核心線程永遠不會銷毀么

這個問題有點tricky。首先我們要明確一下概念,雖然在JavaDoc中也使用了“core/non-core threads”這樣的描述,但其實這是一個動態的概念,JDK并沒有給一部分線程打上“core”的標記,做什么特殊化的處理。這個問題我認為想要探討的是閑置線程終結策略的問題。

在JDK1.6之前,線程池會盡量保持corePoolSize個核心線程,即使這些線程閑置了很長時間。這一點曾被開發者詬病,所以從JDK1.6開始,提供了方法allowsCoreThreadTimeOut,如果傳參為true,則允許閑置的核心線程被終止。

請注意這種策略和corePoolSize=0的區別。我總結的區別是:

corePoolSize=0:在一般情況下只使用一個線程消費任務,只有當并發請求特別多、等待隊列都滿了之后,才開始用多線程。
allowsCoreThreadTimeOut=true && corePoolSize>1:在一般情況下就開始使用多線程(corePoolSize個),當并發請求特別多,等待隊列都滿了之后,繼續加大線程數。但是當請求沒有的時候,允許核心線程也終止。
所以corePoolSize=0的效果,基本等同于allowsCoreThreadTimeOut=true && corePoolSize=1,但實現細節其實不同。

在JDK1.6之后,如果allowsCoreThreadTimeOut=true,核心線程也可以被終止。

4 如何保證線程不被銷毀

首先我們要明確一下線程池模型。線程池有個內部類Worker,它實現了Runnable接口,首先,它自己要run起來。然后它會在合適的時候獲取我們提交的Runnable任務,然后調用任務的run()接口。一個Worker不終止的話可以不斷執行任務。

我們前面說的“線程池中的線程”,其實就是Worker;等待隊列中的元素,是我們提交的Runnable任務。

每一個Worker在創建出來的時候,會調用它本身的run()方法,實現是runWorker(this),這個實現的核心是一個while循環,這個循環不結束,Worker線程就不會終止,就是這個基本邏輯。

在這個while條件中,有個getTask()方法是核心中的核心,它所做的事情就是從等待隊列中取出任務來執行:
如果沒有達到corePoolSize,則創建的Worker在執行完它承接的任務后,會用workQueue.take()取任務、注意,這個接口是阻塞接口,如果取不到任務,Worker線程一直阻塞。
如果超過了corePoolSize,或者allowCoreThreadTimeOut,一個Worker在空閑了之后,會用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)取任務。注意,這個接口只阻塞等待keepAliveTime時間,超過這個時間返回null,則Worker的while循環執行結束,則被終止了。

  1. final void runWorker(Worker w) {        Thread wt = Thread.currentThread();        Runnable task = w.firstTask;        w.firstTask = null;        w.unlock(); // allow interrupts        boolean completedAbruptly = true;        try {            // 看這里,核心邏輯在這里            while (task != null || (task = getTask()) != null) {                w.lock();                // If pool is stopping, ensure thread is interrupted;                // if not, ensure thread is not interrupted.  This                // requires a recheck in second case to deal with                // shutdownNow race while clearing interrupt                if ((runStateAtLeast(ctl.get(), STOP) ||                     (Thread.interrupted() &&                      runStateAtLeast(ctl.get(), STOP))) &&                    !wt.isInterrupted())                    wt.interrupt();                try {                    beforeExecute(wt, task);                    Throwable thrown = null;                    try {                        task.run();                    } catch (RuntimeException x) {                        thrown = x; throw x;                    } catch (Error x) {                        thrown = x; throw x;                    } catch (Throwable x) {                        thrown = x; throw new Error(x);                    } finally {                        afterExecute(task, thrown);                    }                } finally {                    task = null;                    w.completedTasks++;                    w.unlock();                }            }            completedAbruptly = false;        } finally {            processWorkerExit(w, completedAbruptly);        }    } 

  1. private Runnable getTask() {        boolean timedOut = false// Did the last poll() time out?        for (;;) {            int c = ctl.get();            int rs = runStateOf(c);            // Check if queue empty only if necessary.            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {                decrementWorkerCount();                return null;            }            int wc = workerCountOf(c);            // Are workers subject to culling?            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;            if ((wc > maximumPoolSize || (timed && timedOut))                && (wc > 1 || workQueue.isEmpty())) {                if (compareAndDecrementWorkerCount(c))                    return null;                continue;            }            try {                // 注意,核心中的核心在這里                Runnable r = timed ?                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                    workQueue.take();                if (r != null)                    return r;                timedOut = true;            } catch (InterruptedException retry) {                timedOut = false;            }        }    } 

實現方式非常巧妙,核心線程(Worker)即使一直空閑也不終止,是通過workQueue.take()實現的,它會一直阻塞到從等待隊列中取到新的任務。非核心線程空閑指定時間后終止是通過workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)實現的,一個空閑的Worker只等待keepAliveTime,如果還沒有取到任務則循環終止,線程也就運行結束了。

引申思考

Worker本身就是個線程,它再調用我們傳入的Runnable.run(),會啟動一個子線程么?如果你還沒有答案,再回想一下Runnable和Thread的關系。

5 空閑線程過多會有什么問題

籠統地回答是會占用內存,我們分析一下占用了哪些內存。首先,比較普通的一部分,一個線程的內存模型:

虛擬機棧
本地方法棧
程序計數器
我想額外強調是下面這幾個內存占用,需要小心:

ThreadLocal:業務代碼是否使用了ThreadLocal?就算沒有,Spring框架中也大量使用了ThreadLocal,你所在公司的框架可能也是一樣。
局部變量:線程處于阻塞狀態,肯定還有棧幀沒有出棧,棧幀中有局部變量表,凡是被局部變量表引用的內存都不能回收。所以如果這個線程創建了比較大的局部變量,那么這一部分內存無法GC。
TLAB機制:如果你的應用線程數處于高位,那么新的線程初始化可能因為Eden沒有足夠的空間分配TLAB而觸發YoungGC。

線程池保持空閑的核心線程是它的默認配置,一般來講是沒有問題的,因為它占用的內存一般不大。怕的就是業務代碼中使用ThreadLocal緩存的數據過大又不清理。

如果你的應用線程數處于高位,那么需要觀察一下YoungGC的情況,估算一下Eden大小是否足夠。如果不夠的話,可能要謹慎地創建新線程,并且讓空閑的線程終止;必要的時候,可能需要對JVM進行調參。

6 keepAliveTime=0會怎么樣

這也是個爭議點。有的博文說等于0表示空閑線程永遠不會終止,有的說表示執行完立刻終止。還有的說等于-1表示空閑線程永遠不會終止。其實稍微看一下源碼知道了,這里我直接拋出答案。

在JDK1.8中,keepAliveTime=0表示非核心線程執行完立刻終止。

默認情況下,keepAliveTime小于0,初始化的時候才會報錯;但如果allowsCoreThreadTimeOut,keepAliveTime必須大于0,不然初始化報錯。

7 怎么進行異常處理

很多代碼的寫法,我們都習慣按照常見范式去編寫,而沒有去思考為什么。比如:

如果我們使用execute()提交任務,我們一般要在Runable任務的代碼加上try-catch進行異常處理。
如果我們使用submit()提交任務,我們一般要在主線程中,對Future.get()進行try-catch進行異常處理。
—— 但是在上面,我提到過,submit()底層實現依賴execute(),兩者應該統一呀,為什么有差異呢?下面再扒一扒submit()的源碼,它的實現蠻有意思。

首先,ThreadPoolExecutor中沒有submit的代碼,而是在它的父類AbstractExecutorService中,有三個submit的重載方法,代碼非常簡單,關鍵代碼就兩行:

  1. public Future<?> submit(Runnable task) {        if (task == nullthrow new NullPointerException();        RunnableFuture<Void> ftask = newTaskFor(task, null);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Runnable task, T result) {        if (task == nullthrow new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task, result);        execute(ftask);        return ftask;    }    public <T> Future<T> submit(Callable<T> task) {        if (task == nullthrow new NullPointerException();        RunnableFuture<T> ftask = newTaskFor(task);        execute(ftask);        return ftask;    } 

正是因為這三個重載方法,都調用了execute,所以我才說submit底層依賴execute。通過查看這里execute的實現,我們不難發現,它就是ThreadPoolExecutor中的實現,所以,造成submit和execute的差異化的代碼,不在這。那么造成差異的一定在newTaskFor方法中。這個方法也就new了一個FutureTask而已,FutureTask實現RunnableFuture接口,RunnableFuture接口繼承Runnable接口和Future接口。而Callable只是FutureTask的一個成員變量。

所以講到這里,就有另一個Java基礎知識點:Callable和Future的關系。我們一般用Callable編寫任務代碼,Future是異步返回對象,通過它的get方法,阻塞式地獲取結果。FutureTask的核心代碼就是實現了Future接口,也就是get方法的實現:

  1. public V get() throws InterruptedException, ExecutionException {        int s = state;        if (s <= COMPLETING)            // 核心代碼            s = awaitDone(false, 0L);        return report(s);    }    private int awaitDone(boolean timed, long nanos)        throws InterruptedException {        final long deadline = timed ? System.nanoTime() + nanos : 0L;        WaitNode q = null;        boolean queued = false;        // 死循環        for (;;) {            if (Thread.interrupted()) {                removeWaiter(q);                throw new InterruptedException();            }            int s = state;            // 只有任務的狀態是’已完成‘,才會跳出死循環            if (s > COMPLETING) {                if (q != null)                    q.thread = null;                return s;            }            else if (s == COMPLETING) // cannot time out yet                Thread.yield();            else if (q == null)                q = new WaitNode();            else if (!queued)                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,                                                     q.next = waiters, q);            else if (timed) {                nanos = deadline - System.nanoTime();                if (nanos <= 0L) {                    removeWaiter(q);                    return state;                }                LockSupport.parkNanos(this, nanos);            }            else                LockSupport.park(this);        }    } 

get的核心實現是有個awaitDone方法,這是一個死循環,只有任務的狀態是“已完成”,才會跳出死循環;否則會依賴UNSAFE包下的LockSupport.park原語進行阻塞,等待LockSupport.unpark信號量。而這個信號量只有當運行結束獲得結果、或者出現異常的情況下,才會發出來。分別對應方法set和setException。這就是異步執行、阻塞獲取的原理,扯得有點遠了。

回到最初我們的疑問,為什么submit之后,通過get方法可以獲取到異常?原因是FutureTask有一個Object類型的outcome成員變量,用來記錄執行結果。這個結果可以是傳入的泛型,也可以是Throwable異常:

  1. public void run() {        if (state != NEW ||            !UNSAFE.compareAndSwapObject(this, runnerOffset,                                         null, Thread.currentThread()))            return;        try {            Callable<V> c = callable;            if (c != null && state == NEW) {                V result;                boolean ran;                try {                    result = c.call();                    ran = true;                } catch (Throwable ex) {                    result = null;                    ran = false;                    setException(ex);                }                if (ran)                    set(result);            }        } finally {            // runner must be non-null until state is settled to            // prevent concurrent calls to run()            runner = null;            // state must be re-read after nulling runner to prevent            // leaked interrupts            int s = state;            if (s >= INTERRUPTING)                handlePossibleCancellationInterrupt(s);        }    }  // get方法中依賴的,報告執行結果    private V report(int s) throws ExecutionException {        Object x = outcome;        if (s == NORMAL)            return (V)x;        if (s >= CANCELLED)            throw new CancellationException();        throw new ExecutionException((Throwable)x);    } 

FutureTask的另一個巧妙的地方就是借用RunnableAdapter內部類,將submit的Runnable封裝成Callable。所以就算你submit的是Runnable,一樣可以用get獲取到異常。

不論是用execute還是submit,都可以自己在業務代碼上加try-catch進行異常處理。我一般喜歡使用這種方式,因為我喜歡對不同業務場景的異常進行差異化處理,至少打不一樣的日志吧。
如果是execute,還可以自定義線程池,繼承ThreadPoolExecutor并復寫其afterExecute(Runnable r, Throwable t)方法。
或者實現Thread.UncaughtExceptionHandler接口,實現void uncaughtException(Thread t, Throwable e);方法,并將該handler傳遞給線程池的ThreadFactory。
但是注意,afterExecute和UncaughtExceptionHandler都不適用submit。因為通過上面的FutureTask.run()不難發現,它自己對Throwable進行了try-catch,封裝到了outcome屬性,所以底層方法execute的Worker是拿不到異常信息的。

8 線程池需不需要關閉

一般來講,線程池的生命周期跟隨服務的生命周期。如果一個服務(Service)停止服務了,那么需要調用shutdown方法進行關閉。所以ExecutorService.shutdown在Java以及一些中間件的源碼中,是封裝在Service的shutdown方法內的。

如果是Server端不重啟就不停止提供服務,我認為是不需要特殊處理的。

9 shutdown和shutdownNow的區別

shutdown => 平緩關閉,等待所有已添加到線程池中的任務執行完再關閉。
shutdownNow => 立刻關閉,停止正在執行的任務,并返回隊列中未執行的任務。
本來想分析一下兩者的源碼的,但是發現本文的篇幅已經過長了,源碼也貼了不少。感興趣的朋友自己看一下即可。

10 Spring中有哪些和ThreadPoolExecutor類似的工具

這里我想著重強調的就是SimpleAsyncTaskExecutor,Spring中使用的@Async注解,底層就是基于SimpleAsyncTaskExecutor去執行任務,只不過它不是線程池,而是每次都新開一個線程。

另外想要強調的是Executor接口。Java初學者容易想當然的以為Executor結尾的類就是一個線程池,而上面的都是反例。我們可以在JDK的execute方法上看到這個注釋:

  1. /*** Executes the given command at some time in the future.  The command* may execute in a new thread, in a pooled thread, or in the calling* thread, at the discretion of the {@code Executor} implementation.*/ 

所以,它的職責并不是提供一個線程池的接口,而是提供一個“將來執行命令”的接口。真正能代表線程池意義的,是ThreadPoolExecutor類,而不是Executor接口。

最佳實踐總結

【強制】使用ThreadPoolExecutor的構造函數聲明線程池,避免使用Executors類的 newFixedThreadPool和newCachedThreadPool。
【強制】 創建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。即threadFactory參數要構造好。
【建議】建議不同類別的業務用不同的線程池。
【建議】CPU密集型任務(N+1):這種任務消耗的主要是CPU資源,可以將線程數設置為N(CPU核心數)+1,比CPU核心數多出來的一個線程是為了防止線程偶發的缺頁中斷,或者其它原因導致的任務暫停而帶來的影響。一旦任務暫停,CPU就會處于空閑狀態,而在這種情況下多出來的一個線程就可以充分利用CPU的空閑時間。
【建議】I/O密集型任務(2N):這種任務應用起來,系統會用大部分的時間來處理I/O交互,而線程在處理I/O的時間段內不會占用CPU來處理,這時就可以將CPU交出給其它線程使用。因此在I/O密集型任務的應用中,我們可以多配置一些線程,具體的計算方法是2N。
【建議】workQueue不要使用無界隊列,盡量使用有界隊列。避免大量任務等待,造成OOM。
【建議】如果是資源緊張的應用,使用allowsCoreThreadTimeOut可以提高資源利用率。
【建議】雖然使用線程池有多種異常處理的方式,但在任務代碼中,使用try-catch最通用,也能給不同任務的異常處理做精細化。
【建議】對于資源緊張的應用,如果擔心線程池資源使用不當,可以利用ThreadPoolExecutor的API實現簡單的監控,然后進行分析和優化。

線程池初始化示例:

  1. private static final ThreadPoolExecutor pool;    static {        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("po-detail-pool-%d").build();        pool = new ThreadPoolExecutor(48, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(512),            threadFactory, new ThreadPoolExecutor.AbortPolicy());        pool.allowCoreThreadTimeOut(true);    } 

threadFactory:給出帶業務語義的線程命名。
corePoolSize:快速啟動4個線程處理該業務,是足夠的。
maximumPoolSize:IO密集型業務,我的服務器是4C8G的,所以4*2=8。
keepAliveTime:服務器資源緊張,讓空閑的線程快速釋放。
pool.allowCoreThreadTimeOut(true):也是為了在可以的時候,讓線程釋放,釋放資源。
workQueue:一個任務的執行時長在100~300ms,業務高峰期8個線程,按照10s超時(已經很高了)。10s鐘,8個線程,可以處理10 1000ms / 200ms 8 = 400個任務左右,往上再取一點,512已經很多了。
handler:極端情況下,一些任務只能丟棄,保護服務端。

責任編輯:梁菲 來源: 阿里云云棲號
相關推薦

2021-06-01 10:49:22

線程池Java開發

2014-04-17 16:42:03

DevOps

2022-07-26 00:00:22

HTAP系統數據庫

2015-10-20 10:10:51

隱藏功能Windows 10微軟

2021-11-09 09:48:13

Logging python模塊

2021-01-15 07:44:21

SQL注入攻擊黑客

2014-11-28 10:31:07

Hybrid APP

2020-02-27 10:49:26

HTTPS網絡協議TCP

2023-03-16 10:49:55

2019-09-16 08:40:42

2023-10-18 08:04:15

taskworker任務

2012-05-31 09:56:54

云安全

2022-12-12 08:46:11

2022-03-14 07:53:27

ELTETL大數據

2015-07-31 10:35:18

實時計算

2019-11-06 09:52:01

JavaScript單線程非阻塞

2017-10-18 22:01:12

2023-10-24 08:53:24

FutureTas并發編程

2019-07-25 07:42:35

程序員編程語言Python

2025-01-03 08:09:15

點贊
收藏

51CTO技術棧公眾號

三级黄色在线观看| 日韩在线视频在线| 亚洲性猛交富婆| av在线不卡顿| 欧美精品丝袜中出| 国产视频在线观看网站| 水莓100在线视频| 视频一区免费在线观看| 久久精品中文字幕免费mv| 少妇一级淫免费放| 欧美xxx黑人xxx水蜜桃| 久久精品一区蜜桃臀影院| 国产精品一区二区三区成人| 国产极品国产极品| 欧美色资源站| 91国偷自产一区二区开放时间 | 欧美日韩在线一区二区| 久久人妻无码一区二区| 国内在线免费高清视频| 国产精品亚洲一区二区三区在线 | avav成人| 一区二区三区国产| 热re99久久精品国99热蜜月| 国产日韩一级片| 欧美亚洲在线| 色综合91久久精品中文字幕| 永久免费av无码网站性色av| 亚洲性视频在线| 欧美日韩高清一区二区| 欧美色图另类小说| 中文字幕在线三区| 国产精品丝袜91| 久久久综合亚洲91久久98| 国产视频在线免费观看| 日本最新不卡在线| 98视频在线噜噜噜国产| 久久高清内射无套| japanese国产精品| 精品爽片免费看久久| 小泽玛利亚视频在线观看| 国产中文在线播放| 亚洲综合区在线| 一本一道久久a久久综合精品| 亚洲色图欧美视频| 成人国产电影网| 亚洲一区精品电影| 国产一区二区麻豆| 人人精品人人爱| 日本伊人精品一区二区三区介绍 | 亚洲综合99| 欧美国产日韩一区二区| 成年人午夜剧场| 91精品国产91久久久久久黑人| 伊人久久精品视频| 国产成人无码精品久久二区三| 欧美电影在线观看免费| 亚洲国产精品久久久久久| 国产伦理在线观看| 试看120秒一区二区三区| 在线不卡中文字幕播放| 羞羞的视频在线| 国产一区精品福利| 欧美又粗又大又爽| 奇米影音第四色| 福利一区在线| 欧美日韩国产精品自在自线| 日本一二区免费| а天堂中文最新一区二区三区| 欧美日韩免费高清一区色橹橹 | 波多野结衣乳巨码无在线| 国产乱码在线| 五月天一区二区三区| 日本午夜激情视频| 日韩福利一区| 欧美日韩在线亚洲一区蜜芽| 日本国产一级片| 日韩激情精品| 精品成人在线观看| 欧美性xxxx图片| 欧美色网址大全| 日韩在线欧美在线| 欧美色图一区二区| 伊人激情综合| 日韩美女免费线视频| 怡红院男人天堂| 国产一区欧美日韩| 国产精品区二区三区日本| 四虎精品在线| 欧美国产日本视频| 26uuu成人| 成人三级小说| 在线免费不卡视频| 香蕉视频xxx| 国内自拍欧美| 中文字幕精品久久| 青娱乐国产精品| 鲁大师成人一区二区三区| 日韩精品国产欧美| 在线成人一区二区| 在线观看亚洲免费视频| 精品国产乱码| 日韩精品在线观看一区| youjizz亚洲女人| 黄色在线一区| 国产精品一二三视频| 亚洲av综合色区无码一二三区 | 一区不卡字幕| a在线视频v视频| 欧美日韩另类国产亚洲欧美一级| wwwww在线观看| 日韩欧美精品一区| 午夜免费久久久久| 国产有码在线观看| 久久嫩草精品久久久久| 蜜桃网站在线观看| 99久久er| 精品无人区乱码1区2区3区在线| 韩国一级黄色录像| 欧美中文字幕| 成人情视频高清免费观看电影| 8888四色奇米在线观看| 亚洲福利一二三区| 亚洲综合123| 欧美日韩精品一区二区视频| 97香蕉超级碰碰久久免费的优势| 国产精品高潮呻吟av| 久久伊人中文字幕| 日韩欧美精品免费| 精品视频在线一区| 日韩在线视频网| 久久久精品毛片| 91色乱码一区二区三区| 性一交一乱一伧国产女士spa| 亚洲国产综合在线观看| 一本一本久久a久久精品综合小说| 日本熟妇毛耸耸xxxxxx| 国产一区二区三区四区五区美女| 四虎影院一区二区三区| 91久久国产综合久久91猫猫| 亚洲国产成人爱av在线播放| 国内偷拍精品视频| 美女网站色91| 天天综合狠狠精品| 日韩av免费| 亚洲欧洲午夜一线一品| 亚洲久久在线观看| 99久久精品免费看| 欧美人成在线观看| 草莓视频一区二区三区| 久久91亚洲人成电影网站| 国产情侣自拍小视频| 亚洲视频在线观看三级| 亚洲 国产 图片| 国产精品99视频| 国产日韩精品一区二区| 欧美三级理伦电影| 在线播放日韩导航| 欧美视频www| 国产成人日日夜夜| 成人性生活视频免费看| 久久久久97| 欧美怡春院一区二区三区| 亚洲 欧美 自拍偷拍| 欧美日韩性生活视频| 国产激情在线免费观看| 日本网站在线观看一区二区三区| 色综合电影网| 综合久草视频| 欧美猛交ⅹxxx乱大交视频| 亚洲国产综合一区| 五月婷婷综合网| 女同毛片一区二区三区| 日韩av高清在线观看| 一本色道久久综合亚洲精品婷婷| 国产一区二区高清在线| 欧美激情亚洲视频| 亚洲欧美日韩动漫| 岛国av在线不卡| 男人的天堂av网| 国产一区二区在线观看视频| 国产爆乳无码一区二区麻豆 | 国产精选一区二区三区| 国产精品三级一区二区| 西瓜成人精品人成网站| 国产乱肥老妇国产一区二 | 亚洲人成色777777精品音频| 色88888久久久久久影院野外| 中文字幕第69页| 丰满白嫩尤物一区二区| www.com毛片| 天天操综合网| 精品无人区一区二区三区| а√天堂资源国产精品| 欧美久久精品午夜青青大伊人| 国模人体一区二区| 欧美三级中文字幕在线观看| 激情综合网五月天| 国产婷婷色一区二区三区在线| 中文字幕欧美视频| 久久久久久穴| 黄色特一级视频| 国产精品一区二区99| 亚洲www视频| 偷拍中文亚洲欧美动漫| 欧美高清不卡在线| h视频在线免费| 亚洲第一视频网站| 国产理论片在线观看| 精品久久久久久久久久久久久久| 欧美日韩国产一二三区| 91亚洲国产成人精品一区二三 | 成人av电影在线观看| 爱情岛论坛vip永久入口| 在线成人h网| 日本三级福利片| 国产免费久久| 精品视频第一区| 91麻豆精品国产91久久久久推荐资源| 国产精品老牛影院在线观看| 男女羞羞在线观看| 欧美成人剧情片在线观看| av成人手机在线| 亚洲精品永久免费精品| 日韩在线一区二区三区四区| 欧美一区二区在线播放| 一区二区的视频| 欧美无乱码久久久免费午夜一区| 国产乱国产乱老熟| 亚洲一区二区在线免费看| 国产中文av在线| 中文天堂在线一区| 色一情一交一乱一区二区三区| 99精品国产热久久91蜜凸| 91亚洲一线产区二线产区| 久久99精品网久久| 我看黄色一级片| 日本特黄久久久高潮| 国产一级做a爰片久久| 日韩电影网1区2区| 黄色av免费在线播放| 性8sex亚洲区入口| 两根大肉大捧一进一出好爽视频| 激情国产一区| 大伊香蕉精品视频在线| 欧美日韩亚洲国产精品| 肉大捧一出免费观看网站在线播放 | av资源在线免费观看| 国产精品伦理在线| 欧美日韩国产黄色| 国产精品理论片| 毛片久久久久久| 亚洲欧美影音先锋| 尤物在线免费视频| 又紧又大又爽精品一区二区| 久久r这里只有精品| 一区二区三区在线高清| 国产一级做a爰片在线看免费| 亚洲国产精品天堂| 日韩精品一区二区在线播放 | 一级片aaaa| 在线成人小视频| 朝桐光av在线一区二区三区| 精品久久久久久久久久久久久久久 | av一区二区不卡| 国产精品揄拍100视频| 久久精品免费在线观看| 99国产精品无码| 亚洲精品你懂的| 欧美成人三级在线观看| 亚洲丰满少妇videoshd| 免费黄色网址在线| 精品1区2区3区| www.中文字幕| 精品无人区太爽高潮在线播放| 岛国在线大片| 久久综合伊人77777| 岛国片av在线| 国产精品海角社区在线观看| 国产精品亚洲综合在线观看 | 国产精品99久久久久久人| 久久国产三级| 国产精品久久久久久久久婷婷 | 精品国内亚洲在观看18黄| aa在线视频| 欧美又大粗又爽又黄大片视频| 黄色成人在线观看网站| 97视频中文字幕| 九九在线高清精品视频| 在线播放 亚洲| aa亚洲婷婷| 亚洲精品mv在线观看| 91亚洲资源网| 五月婷婷一区二区| 色老头久久综合| 精品人妻一区二区三区含羞草 | 国产高清自拍99| 精品国产乱码久久久久久1区2匹| 红桃一区二区三区| 日日夜夜精品视频免费| 中文写幕一区二区三区免费观成熟| 9人人澡人人爽人人精品| 国产三级在线观看完整版| 亚洲 欧美综合在线网络| 一二三四区在线| 亚洲久久久久久久久久久| 久久亚洲天堂| 日本在线观看天堂男亚洲| 99a精品视频在线观看| 色视频一区二区三区| 亚洲色诱最新| 国产探花一区二区三区| 国产免费成人在线视频| 亚洲一区欧美在线| 91精品国产综合久久小美女| 黄色av网址在线免费观看| 久久久久久国产精品| 在线免费成人| 亚洲国产精品一区在线观看不卡| www日韩tube| 欧美精品第一页在线播放| 欧美久久久网站| 欧美日韩视频在线一区二区观看视频| 欧美日韩国内| 亚洲天堂网站在线| 国产精品国产三级国产aⅴ入口 | 日韩欧美一区二区视频| 成人亚洲综合天堂| 欧美一性一乱一交一视频| 久久久久久久久久久久久久久久久久久久| 三年中国中文在线观看免费播放| 久久久久久久高潮| 国内精品久久99人妻无码| 亚洲国产日韩综合久久精品| av网站免费播放| 久久久精品视频在线观看| 精品三级在线| 亚洲巨乳在线观看| 日本免费新一区视频| 日韩乱码人妻无码中文字幕久久| 五月天欧美精品| 天天干天天插天天操| 亚洲91av视频| 免费日韩一区二区三区| 欧美在线一区视频| 国产.精品.日韩.另类.中文.在线.播放 | 精品国产一区二区三区成人影院 | 成人av先锋影音| 精品午夜福利视频| 精品久久国产老人久久综合| 波多野结衣在线观看| 国产伦精品一区二区三区免| 最新亚洲一区| 国模私拍在线观看| 欧美日韩国产精品| 少妇人妻精品一区二区三区| 91黑丝高跟在线| 久久av影视| 三级在线免费看| 国产精品嫩草影院com| 91成人在线免费| 欧美理论电影在线播放| 国产区精品视频在线观看豆花| 美女日批免费视频| 国产亚洲一区二区在线观看| 在线观看视频中文字幕| 久久精品国产一区二区电影| 视频亚洲一区二区| 亚洲人精品午夜射精日韩| 久久综合av免费| ,一级淫片a看免费| 久久99热精品这里久久精品| 极品尤物一区| 九一精品在线观看| 亚洲少妇屁股交4| 日本黄色大片视频| 日韩av第一页| 99久久久国产精品美女| 中文字幕无码毛片免费看| 欧美日韩国产中文字幕 | 中文字幕一区二区三中文字幕| 国产丰满美女做爰| 91成人免费观看网站| 成人在线免费观看91| 三上悠亚 电影| 色综合亚洲欧洲| sm国产在线调教视频| 精品久久久久久乱码天堂| 另类成人小视频在线| 日本少妇激情视频| 色黄久久久久久| 麻豆一区一区三区四区| 亚洲第一狼人区| 亚洲午夜在线观看视频在线| 极品白浆推特女神在线观看 | 538任你躁在线精品免费| 亚洲国产婷婷综合在线精品| 9色在线观看| 狠狠久久综合婷婷不卡| 精品在线免费视频| 成年人免费高清视频|