日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2万字,看完这篇才敢说自己真的懂线程池!

發布時間:2025/3/11 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2万字,看完这篇才敢说自己真的懂线程池! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

線程池可以說是 Java 進階必備的知識點了,也是面試中必備的考點,可能不少人看了一些文章后能對線程池工作原理說上一二,但這還遠遠不夠,如果碰到比較有經驗的面試官再繼續追問,很可能會被吊打,考慮如下問題:

  • Tomcat 的線程池和 JDK 的線程池實現有啥區別, Dubbo 中有類似 Tomcat 的線程池實現嗎?

  • 我司網關 dubbo 調用線程池曾經出現過這樣的一個問題:壓測時接口可以正常返回,但接口 RT 很高,假設設置的核心線程大小為 500,最大線程為 800,緩沖隊列為 5000,你能從這個設置中發現出一些問題并對這些參數進行調優嗎?

  • 線程池里的線程真的有核心線程和非核心線程之分?

  • 線程池被 shutdown 后,還能產生新的線程?

  • 線程把任務丟給線程池后肯定就馬上返回了?

  • 線程池里的線程異常后會再次新增線程嗎,如何捕獲這些線程拋出的異常?

  • 線程池的大小如何設置,如何動態設置線程池的參數

  • 線程池的狀態機畫一下?

  • 阿里 Java 代碼規范為什么不允許使用 Executors 快速創建線程池?

  • 使用線程池應該避免哪些問題,能否簡單說下線程池的最佳實踐?

  • 如何優雅關閉線程池

  • 如何對線程池進行監控

  • 相信不少人看了這些問題會有些懵逼

    其實這些問題的答案大多數都藏在線程池的源碼里,所以深入了解線程池的源碼非常重要,本章我們將會來學習一下線程池的源碼,相信看完之后,以上的問題大部分都能回答,另外一些問題我們也會在文中與大家一起探討。

    本文將會從以下幾個方面來介紹線程池的原理。

  • 為什么要用線程池

  • 線程池是如何工作的

  • 線程池提交任務的兩種方式

  • ThreadPoolExecutor 源碼剖析

  • 解答開篇的問題

  • 線程池的最佳實踐

  • 總結

  • 相信大家看完對線程池的理解會更進一步,肝文不易,看完別完了三連哦。

    為什么要用線程池

    創建線程有三大開銷,如下:

    1、其實 Java 中的線程模型是基于操作系統原生線程模型實現的,也就是說 Java 中的線程其實是基于內核線程實現的,線程的創建,析構與同步都需要進行系統調用,而系統調用需要在用戶態與內核中來回切換,代價相對較高,線程的生命周期包括「線程創建時間」,「線程執行任務時間」,「線程銷毀時間」,創建和銷毀都需要導致系統調用。2、每個 Thread 都需要有一個內核線程的支持,也就意味著每個 Thread 都需要消耗一定的內核資源(如內核線程的棧空間),因此能創建的 Thread 是有限的,默認一個線程的線程棧大小是 1 M,有圖有真相

    圖中所示,在 Java 8 下,創建 19 個線程(thread #19)需要創建 19535 KB,即 1 M 左右,reserved 代表如果創建 19 個線程,操作系統保證會為其分配這么多空間(實際上并不一定分配),committed 則表示實際已分配的空間大小。

    畫外音:注意,這是在 Java 8 下的線程占用空間情況,但在 Java 11 中,對線程作了很大的優化,創建一個線程大概只需要 40 KB,空間消耗大大減少

    3、線程多了,導致不可忽視的上下文切換開銷。

    由此可見,線程的創建是昂貴的,所以必須以線程池的形式來管理這些線程,在線程池中合理設置線程大小和管理線程,以達到以合理的創建線程大小以達到最大化收益,最小化風險的目的,對于開發人員來說,要完成任務不用關心線程如何創建,如何銷毀,如何協作,只需要關心提交的任務何時完成即可,對線程的調優,監控等這些細枝末節的工作通通交給線程池來實現,所以也讓開發人員得到極大的解脫!

    類似線程池的這種池化思想應用在很多地方,比如數據庫連接池,Http 連接池等,避免了昂貴資源的創建,提升了性能,也解放了開發人員。

    ThreadPoolExecutor 設計架構圖

    首先我們來看看 Executor 框架的設計圖

    • Executor: 最頂層的 Executor 接口只提供了一個 execute 接口,實現了提交任務與執行任務的解藕,這個方法是最核心的,也是我們源碼剖析的重點,此方法最終是由 ThreadPoolExecutor 實現的,

    • ExecutorService 擴展了 Executor 接口,實現了終止執行器,單個/批量提交任務等方法

    • AbstractExecutorService 實現了 ExecutorService 接口,實現了除 execute 以外的所有方法,只將一個最重要的 execute 方法交給 ThreadPoolExecutor 實現。

    這樣的分層設計雖然層次看起來挺多,但每一層每司其職,邏輯清晰,值得借鑒。

    線程池是如何工作的

    首先我們來看下如何創建一個線程池

    ThreadPoolExecutor?threadPool?=?new?ThreadPoolExecutor(10,?20,?600L,TimeUnit.SECONDS,?new?LinkedBlockingQueue<>(4096),new?NamedThreadFactory("common-work-thread")); //?設置拒絕策略,默認為?AbortPolicy threadPool.setRejectedExecutionHandler(new?ThreadPoolExecutor.AbortPolicy());

    看下其構造方法簽名如下

    public?ThreadPoolExecutor(int?corePoolSize,int?maximumPoolSize,long?keepAliveTime,TimeUnit?unit,BlockingQueue<Runnable>?workQueue,ThreadFactory?threadFactory,RejectedExecutionHandler?handler)?{//?省略代碼若干 }

    要理解這些參數具體代表的意義,必須清楚線程池提交任務與執行任務流程,如下

    圖片來自美團技術團隊

    步驟如下

    1、corePoolSize:如果提交任務后線程還在運行,當線程數小于 corePoolSize 值時,無論線程池中的線程是否忙碌,都會創建線程,并把任務交給此新創建的線程進行處理,如果線程數少于等于 corePoolSize,那么這些線程不會回收,除非將 allowCoreThreadTimeOut 設置為 true,但一般不這么干,因為頻繁地創建銷毀線程會極大地增加系統調用的開銷。

    2、workQueue:如果線程數大于核心數(corePoolSize)且小于最大線程數(maximumPoolSize),則會將任務先丟到阻塞隊列里,然后線程自己去阻塞隊列中拉取任務執行。

    3、maximumPoolSize: 線程池中最大可創建的線程數,如果提交任務時隊列滿了且線程數未到達這個設定值,則會創建線程并執行此次提交的任務,如果提交任務時隊列滿了但線池數已經到達了這個值,此時說明已經超出了線池程的負載能力,就會執行拒絕策略,這也好理解,總不能讓源源不斷地任務進來把線程池給壓垮了吧,我們首先要保證線程池能正常工作。

    4、RejectedExecutionHandler:一共有以下四種拒絕策略

    • AbortPolicy:丟棄任務并拋出異常,這也是默認策略;

    • CallerRunsPolicy:用調用者所在的線程來執行任務,所以開頭的問題「線程把任務丟給線程池后肯定就馬上返回了?」我們可以回答了,如果用的是 CallerRunsPolicy 策略,提交任務的線程(比如主線程)提交任務后并不能保證馬上就返回,當觸發了這個 reject 策略不得不親自來處理這個任務。

    • DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,并執行當前任務。

    • DiscardPolicy:直接丟棄任務,不拋出任何異常,這種策略只適用于不重要的任務。

    5、keepAliveTime: 線程存活時間,如果在此時間內超出 corePoolSize 大小的線程處于 idle 狀態,這些線程會被回收

    6、threadFactory:可以用此參數設置線程池的命名,指定 defaultUncaughtExceptionHandler(有啥用,后文闡述),甚至可以設定線程為守護線程。

    現在問題來了,該如何合理設置這些參數呢。

    首先來看線程大小設置

    <<Java 并發編程實戰>>告訴我們應該分兩種情況

  • 針對 CPU 密集型的任務,在有 Ncpu個處理器的系統上,當線程池的大小為 Ncpu + 1 時,通常能實現最優的利用率,+1 是因為當計算密集型線程偶爾由于缺頁故障或其他原因而暫停工作時,這個"額外"的線程也能確保 CPU 的時鐘周期不會被浪費,所謂 CPU 密集,就是線程一直在忙碌,這樣將線程池的大小設置為 Ncpu + 1 避免了線程的上下文切換,讓線程時刻處于忙碌狀態,將 CPU 的利用率最大化。

  • 針對 IO 密集型的任務,它也給出了如下計算公式

  • 這些公式看看就好,實際的業務場景中基本用不上,這些公式太過理論化了,脫離業務場景,僅可作個理論參考,舉個例子,你說 CPU 密集型任務設置線程池大小為 N + 1個,但實際上在業務中往往不只設置一個線程池,這種情況套用的公式就懵逼了

    再來看 workQueue 的大小設置

    由上文可知,如果最大線程大于核心線程數,當且僅當核心線程滿了且 workQueue 也滿的情況下,才會新增新的線程,也就是說如果 workQueue 是無界隊列,那么當線程數增加到 corePoolSize 后,永遠不會再新增新的線程了,也就是說此時 maximumPoolSize 的設置就無效了,也無法觸發 RejectedExecutionHandler 拒絕策略,任務只會源源不斷地填充到 workQueue,直到 OOM。

    所以 workQueue 應該為有界隊列,至少保證在任務過載的情況下線程池還能正常工作,那么哪些是有有界隊列,哪些是無界隊列呢。

    有界隊列我們常用的以下兩個

    • LinkedBlockingQueue: 鏈表構成的有界隊列,按先進先出(FIFO)的順序對元素進行排列,但注意在創建時需指定其大小,否則其大小默認為 Integer.MAX_VALUE,相當于無界隊列了

    • ArrayBlockingQueue: 數組實現的有界隊列,按先進先出(FIFO)的順序對元素進行排列。

    無界隊列我們常用 PriorityBlockingQueue 這個優先級隊列,任務插入的時候可以指定其權重以讓這些任務優先執行,但這個隊列很少用,原因很簡單,線程池里的任務執行順序一般是平等的,如果真有必須某些類型的任務需要優先執行,大不了再開個線程池好了,將不同的任務類型用不同的線程池隔離開來,也是合理利用線程池的一種實踐。

    說到這我相信大家應該能回答開頭的問題「阿里 Java 代碼規范為什么不允許使用 Executors 快速創建線程池?」,最常見的是以下兩種創建方式

    image-20201109002227476

    newCachedThreadPool 方法的最大線程數設置成了 Integer.MAX_VALUE,而 newSingleThreadExecutor 方法創建 workQueue 時 LinkedBlockingQueue 未聲明大小,相當于創建了無界隊列,一不小心就會導致 OOM。

    threadFactory 如何設置

    一般業務中會有多個線程池,如果某個線程池出現了問題,定位是哪一個線程出問題很重要,所以為每個線程池取一個名字就很有必要了,我司用的 dubbo 的 NamedThreadFactory 來生成 threadFactory,創建很簡單

    new?NamedThreadFactory("demo-work")

    它的實現還是很巧妙的,有興趣地可以看看它的源碼,每調用一次,底層有個計數器會加一,會依次命名為 「demo-work-thread-1」, 「demo-work-thread-2」, 「demo-work-thread-3」這樣遞增的字符串。

    在實際的業務場景中,一般很難確定 corePoolSize, workQueue,maximumPoolSize 的大小,如果出問題了,一般來說只能重新設置一下這些參數再發布,這樣往往需要耗費一些時間,美團的這篇文章給出了讓人眼前一亮的解決方案,當發現問題(線程池監控告警)時,動態調整這些參數,可以讓這些參數實時生效,能在發現問題時及時解決,確實是個很好的思路。

    線程池提交任務的兩種方式

    線程池創建好了,該怎么給它提交任務,有兩種方式,調用 execute 和 submit 方法,來看下這兩個方法的方法簽名

    //?方式一:execute 方法 public?void?execute(Runnable?command)?{ }//?方式二:ExecutorService 中 submit 的三個方法 <T>?Future<T>?submit(Callable<T>?task); <T>?Future<T>?submit(Runnable?task,?T?result); Future<?>?submit(Runnable?task);

    區別在于調用 execute 無返回值,而調用 ?submit 可以返回 Future,那么這個 Future 能到底能干啥呢,看它的接口

    public?interface?Future<V>?{/***?取消正在執行的任務,如果任務已執行或已被取消,或者由于某些原因不能取消則返回?false*?如果任務未開始或者任務已開始但可以中斷(mayInterruptIfRunning?為?true),則*?可以取消/中斷此任務*/boolean?cancel(boolean?mayInterruptIfRunning);/***?任務在完成前是否已被取消*/boolean?isCancelled();/***?正常的執行完流程流程,或拋出異常,或取消導致的任務完成都會返回?true*/boolean?isDone();/***?阻塞等待任務的執行結果*/V?get()?throws?InterruptedException,?ExecutionException;/***?阻塞等待任務的執行結果,不過這里指定了時間,如果在?timeout?時間內任務還未執行完成,*?則拋出?TimeoutException?異常*/V?get(long?timeout,?TimeUnit?unit)throws?InterruptedException,?ExecutionException,?TimeoutException; }

    可以用 Future 取消任務,判斷任務是否已取消/完成,甚至可以阻塞等待結果。

    submit 為啥能提交任務(Runnable)的同時也能返回任務(Future)的執行結果呢

    原來在最后執行 execute 前用 newTaskFor 將 task 封裝成了 RunnableFuture,newTaskFor 返回了 FutureTask 這個類,結構圖如下

    可以看到 FutureTask 這個接口既實現了 Runnable 接口,也實現 Future 接口,所以在提交任務的同時也能利用 Future 接口來執行任務的取消,獲取任務的狀態,等待執行結果這些操作。

    execute 與 submit 除了是否能返回執行結果這一區別外,還有一個重要區別,那就是使用 execute 執行如果發生了異常,是捕獲不到的,默認會執行 ThreadGroup 的 uncaughtException 方法(下圖數字 2 對應的邏輯)

    所以如果你想監控執行 execute 方法時發生的異常,需要通過 threadFactory 來指定一個 UncaughtExceptionHandler,這樣就會執行上圖中的 1,進而執行 UncaughtExceptionHandler 中的邏輯,如下所示:

    //1.實現一個自己的線程池工廠 ThreadFactory?factory?=?(Runnable?r)?->?{//創建一個線程Thread?t?=?new?Thread(r);//給創建的線程設置UncaughtExceptionHandler對象?里面實現異常的默認邏輯t.setDefaultUncaughtExceptionHandler((Thread?thread1,?Throwable?e)?->?{//?在此設置統計監控邏輯System.out.println("線程工廠設置的exceptionHandler"?+?e.getMessage());});return?t; };//?2.創建一個自己定義的線程池,使用自己定義的線程工廠 ExecutorService?service?=?new?ThreadPoolExecutor(1,?1,?0,?TimeUnit.MILLISECONDS,new?LinkedBlockingQueue(10),factory);//3.提交任務 service.execute(()->{int?i=1/0; });

    執行以上邏輯最終會輸出「線程工廠設置的exceptionHandler/ by zero」,通過這樣的方式就能通過設定的 defaultUncaughtExceptionHandler 來執行我們的監控邏輯了。

    如果用 submit ,如何捕獲異常呢,當我們調用 future.get 就可以捕獲

    Callable?testCallable?=?xxx; Future?future?=?executor.submit(myCallable); try?{future1.get(3)); }?catch?(InterruptedException?e)?{e.printStackTrace(); }?catch?(ExecutionException?e)?{e.printStackTrace(); }

    那么 future 為啥在 get 的時候才捕獲異步呢,因為在執行 submit 時拋出異常后此異常被保存了起來,而在 get 的時候才被拋出

    關于 execute 和 submit 的執行流程 why 神的這篇文章寫得非常透徹,我就不拾人牙慧了,建議大家好好品品,收獲會很大!

    ThreadPoolExecutor 源碼剖析

    前面鋪墊了這么多,終于到了最核心的源碼剖析環節了。

    對于線程池來說,我們最關心的是它的「狀態」和「可運行的線程數量」,一般來說我們可以選擇用兩個變量來記錄,不過 Doug Lea 只用了一個變量(ctl)就達成目的了,我們知道變量越多,代碼的可維護性就越差,也越容易出 bug, 所以只用一個變量就達成了兩個變量的效果,這讓代碼的可維護性大大提高,那么他是怎么設計的呢

    //?ThreadPoolExecutor.java public?class?ThreadPoolExecutor?extends?AbstractExecutorService?{private?final?AtomicInteger?ctl?=?new?AtomicInteger(ctlOf(RUNNING,?0));private?static?final?int?COUNT_BITS?=?Integer.SIZE?-?3;private?static?final?int?CAPACITY???=?(1?<<?COUNT_BITS)?-?1;//?結果:111 00000000000000000000000000000private?static?final?int?RUNNING????=?-1?<<?COUNT_BITS;//?結果:?000?00000000000000000000000000000private?static?final?int?SHUTDOWN???=??0?<<?COUNT_BITS;//?結果:?001 00000000000000000000000000000private?static?final?int?STOP???????=??1?<<?COUNT_BITS;//?結果:?010?00000000000000000000000000000private?static?final?int?TIDYING????=??2?<<?COUNT_BITS;//?結果:?011 00000000000000000000000000000private?static?final?int?TERMINATED?=??3?<<?COUNT_BITS;//?獲取線程池的狀態private?static?int?runStateOf(int?c)?????{?return?c?&?~CAPACITY;?}//?獲取線程數量private?static?int?workerCountOf(int?c)??{?return?c?&?CAPACITY;?} }

    可以看到,ctl 是一個 原子類的 Integer 變量,有 32 位,低 29 位表示線程數量, 29 位最大可以表示 (2^29)-1 (大概 5 億多),足夠記錄線程大小了,如果未來還是不夠,可以把 ctl 聲明為 AtomicLong,高 3 位用來表示線程池的狀態,3 位可以表示 8 個線程池的狀態,由于線程池總共只有五個狀態,所以 3 位也是足夠了,線程池的五個狀態如下

    • RUNNING: 接收新的任務,并能繼續處理 workQueue 中的任務

    • SHUTDOWN: 不再接收新的任務,不過能繼續處理 workQueue 中的任務

    • STOP: 不再接收新的任務,也不再處理 workQueue 中的任務,并且會中斷正在處理任務的線程

    • TIDYING: 所有的任務都完結了,并且線程數量(workCount)為 0 時即為此狀態,進入此狀態后會調用 terminated() 這個鉤子方法進入 TERMINATED 狀態

    • TERMINATED: 調用 terminated() 方法后即為此狀態

    線程池的狀態流轉及觸發條件如下

    有了這些基礎,我們來分析下 execute 的源碼

    public?void?execute(Runnable?command)?{if?(command?==?null)throw?new?NullPointerException();int?c?=?ctl.get();//?如果當前線程數少于核心線程數(corePoolSize),無論核心線程是否忙碌,都創建線程,直到達到?corePoolSize?為止if?(workerCountOf(c)?<?corePoolSize)?{//?創建線程并將此任務交給?worker?處理(此時此任務即?worker?中的?firstTask)if?(addWorker(command,?true))return;c?=?ctl.get();}//?如果線程池處于?RUNNING?狀態,并且線程數大于?corePoolSize?或者?//?線程數少于?corePoolSize?但創建線程失敗了,則將任務丟進?workQueue?中if?(isRunning(c)?&&?workQueue.offer(command))?{int?recheck?=?ctl.get();//?這里需要再次檢查線程池是否處于?RUNNING?狀態,因為在任務入隊后可能線程池狀態會發生變化,(比如調用了?shutdown?方法等),如果線程狀態發生變化了,則移除此任務,執行拒絕策略if?(!?isRunning(recheck)?&&?remove(command))reject(command);//?如果線程池在?RUNNING?狀態下,線程數為?0,則新建線程加速處理?workQueue?中的任務else?if?(workerCountOf(recheck)?==?0)addWorker(null,?false);}//?這段邏輯說明線程數大于?corePoolSize?且任務入隊失敗了,此時會以最大線程數(maximumPoolSize)為界來創建線程,如果失敗,說明線程數超過了?maximumPoolSize,則執行拒絕策略else?if?(!addWorker(command,?false))reject(command); }

    從這段代碼中可以看到,創建線程是調用 addWorker 實現的,在分析 addWorker 之前,有必要簡單提一下 Worker,線程池把每一個執行任務的線程都封裝為 Worker 的形式,取名為 Worker 很形象,線程池的本質是生產者-消費者模型,生產者不斷地往 workQueue 中丟 task, workQueue 就像流水線一樣不斷地輸送著任務,而 worker(工人) 不斷地取任務來執行

    那么問題來了,為啥要把線程封裝到 worker 中呢,線程池拿到 task 后直接丟給線程處理或者讓線程自己去 workQueue 中處理不就完了?

    將線程封裝為 worker 主要是為了更好地管理線程的中斷

    來看下 Worker 的定義

    //?此處可以看出?worker?既是一個?Runnable?任務,也實現了?AQS(實際上是用?AQS?實現了一個獨占鎖,這樣由于?worker?運行時會上鎖,執行?shutdown,setCorePoolSize,setMaximumPoolSize等方法時會試著中斷線程(interruptIdleWorkers)?,在這個方法中斷方法中會先嘗試獲取?worker?的鎖,如果不成功,說明?worker?在運行中,此時會先讓?worker?執行完任務再關閉?worker?的線程,實現優雅關閉線程的目的) private?final?class?Workerextends?AbstractQueuedSynchronizerimplements?Runnable{private?static?final?long?serialVersionUID?=?6138294804551838833L;//?實際執行任務的線程final?Thread?thread;//?上文提到,如果當前線程數少于核心線程數,創建線程并將提交的任務交給?worker?處理處理,此時?firstTask?即為此提交的任務,如果?worker?從?workQueue?中獲取任務,則?firstTask?為空Runnable?firstTask;//?統計完成的任務數volatile?long?completedTasks;Worker(Runnable?firstTask)?{//?初始化為?-1,這樣在線程運行前(調用runWorker)禁止中斷,在?interruptIfStarted()?方法中會判斷?getState()>=0setState(-1);?this.firstTask?=?firstTask;//?根據線程池的?threadFactory?創建一個線程,將?worker?本身傳給線程(因為?worker?實現了?Runnable?接口)this.thread?=?getThreadFactory().newThread(this);}public?void?run()?{//?thread?啟動后會調用此方法runWorker(this);}//?1?代表被鎖住了,0?代表未鎖protected?boolean?isHeldExclusively()?{return?getState()?!=?0;}//?嘗試獲取鎖protected?boolean?tryAcquire(int?unused)?{//?從這里可以看出它是一個獨占鎖,因為當獲取鎖后,cas?設置?state?不可能成功,這里我們也能明白上文中將?state?設置為?-1?的作用,這種情況下永遠不可能獲取得鎖,而?worker?要被中斷首先必須獲取鎖if?(compareAndSetState(0,?1))?{setExclusiveOwnerThread(Thread.currentThread());return?true;}return?false;}//?嘗試釋放鎖protected?boolean?tryRelease(int?unused)?{setExclusiveOwnerThread(null);setState(0);return?true;}????public?void?lock()????????{?acquire(1);?}public?boolean?tryLock()??{?return?tryAcquire(1);?}public?void?unlock()??????{?release(1);?}public?boolean?isLocked()?{?return?isHeldExclusively();?}//?中斷線程,這個方法會被?shutdowNow?調用,從中可以看出?shutdownNow?要中斷線程不需要獲取鎖,也就是說如果線程正在運行,照樣會給你中斷掉,所以一般來說我們不用?shutdowNow?來中斷線程,太粗暴了,中斷時線程很可能在執行任務,影響任務執行void?interruptIfStarted()?{Thread?t;//?中斷也是有條件的,必須是?state?>=?0?且?t?!=?null?且線程未被中斷//?如果?state?==?-1?,不執行中斷,再次明白了為啥上文中?setState(-1)?的意義if?(getState()?>=?0?&&?(t?=?thread)?!=?null?&&?!t.isInterrupted())?{try?{t.interrupt();}?catch?(SecurityException?ignore)?{}}}}

    通過上文對 Worker 類的分析,相信大家不難理解 將線程封裝為 worker 主要是為了更好地管理線程的中斷 這句話。

    理解了 Worker 的意義,我們再來看 addWorker 的方法

    private?boolean?addWorker(Runnable?firstTask,?boolean?core)?{retry:for?(;;)?{int?c?=?ctl.get();//?獲取線程池的狀態int?rs?=?runStateOf(c);//?如果線程池的狀態?>=?SHUTDOWN,即為?SHUTDOWN,STOP,TIDYING,TERMINATED?這四個狀態,只有一種情況有可能創建線程,即線程狀態為?SHUTDOWN,?且隊列非空時,firstTask?==?null?代表創建一個不接收新任務的線程(此線程會從?workQueue?中獲取任務再執行),這種情況下創建線程是為了加速處理完?workQueue?中的任務if?(rs?>=?SHUTDOWN?&&!?(rs?==?SHUTDOWN?&&firstTask?==?null?&&!?workQueue.isEmpty()))return?false;for?(;;)?{//?獲取線程數int?wc?=?workerCountOf(c);//?如果超過了線程池的最大?CAPACITY(5?億多,基本不可能)//?或者?超過了?corePoolSize(core?為?true)?或者?maximumPoolSize(core?為?false)?時//?則返回?falseif?(wc?>=?CAPACITY?||wc?>=?(core???corePoolSize?:?maximumPoolSize))return?false;//?否則?CAS?增加線程的數量,如果成功跳出雙重循環if?(compareAndIncrementWorkerCount(c))break?retry;c?=?ctl.get();??//?Re-read?ctl//?如果線程運行狀態發生變化,跳到外層循環繼續執行if?(runStateOf(c)?!=?rs)continue?retry;//?說明是因為?CAS?增加線程數量失敗所致,繼續執行?retry?的內層循環}}boolean?workerStarted?=?false;boolean?workerAdded?=?false;Worker?w?=?null;try?{//?能執行到這里,說明滿足增加?worker?的條件了,所以創建?worker,準備添加進線程池中執行任務w?=?new?Worker(firstTask);final?Thread?t?=?w.thread;if?(t?!=?null)?{//?加鎖,是因為下文要把?w?添加進?workers?中,?workers?是?HashSet,不是線程安全的,所以需要加鎖予以保證final?ReentrantLock?mainLock?=?this.mainLock;mainLock.lock();try?{//??再次?check?線程池的狀態以防執行到此步時發生中斷等int?rs?=?runStateOf(ctl.get());//?如果線程池狀態小于?SHUTDOWN(即為?RUNNING),//?或者狀態為?SHUTDOWN?但?firstTask?==?null(代表不接收任務,只是創建線程處理?workQueue?中的任務),則滿足添加?worker?的條件if?(rs?<?SHUTDOWN?||(rs?==?SHUTDOWN?&&?firstTask?==?null))?{//?如果線程已啟動,顯然有問題(因為創建?worker?后,還沒啟動線程呢),拋出異常if?(t.isAlive())?throw?new?IllegalThreadStateException();workers.add(w);int?s?=?workers.size();//?記錄最大的線程池大小以作監控之用if?(s?>?largestPoolSize)largestPoolSize?=?s;workerAdded?=?true;}}?finally?{mainLock.unlock();}//?說明往?workers?中添加?worker?成功,此時啟動線程if?(workerAdded)?{t.start();workerStarted?=?true;}}}?finally?{//?添加線程失敗,執行?addWorkerFailed?方法,主要做了將?worker?從?workers?中移除,減少線程數,并嘗試著關閉線程池這樣的操作if?(!?workerStarted)addWorkerFailed(w);}return?workerStarted; }

    從這段代碼我們可以看到多線程下情況的不可預料性,我們發現在滿足條件情況下,又對線程狀態重新進行了 check,以防期間出現中斷等線程池狀態發生變更的操作,這也給我們以啟發:多線程環境下的各種臨界條件一定要考慮到位。

    執行 addWorker 創建 worker 成功后,線程開始執行了(t.start()),由于在創建 Worker 時,將 Worker ?自己傳給了此線程,所以啟動線程后,會調用 ?Worker 的 run 方法

    public?void?run()?{runWorker(this); }

    可以看到最終會調用 ?runWorker 方法,接下來我們來分析下 runWorker 方法

    final?void?runWorker(Worker?w)?{Thread?wt?=?Thread.currentThread();Runnable?task?=?w.firstTask;w.firstTask?=?null;//?unlock?會調用?tryRelease?方法將?state?設置成?0,代表允許中斷,允許中斷的條件上文我們在?interruptIfStarted()?中有提過,即?state?>=?0w.unlock();boolean?completedAbruptly?=?true;try?{//?如果在提交任務時創建了線程,并把任務丟給此線程,則會先執行此?task//?否則從任務隊列中獲取?task?來執行(即?getTask()?方法)while?(task?!=?null?||?(task?=?getTask())?!=?null)?{w.lock();//?如果線程池狀態為?>=?STOP(即?STOP,TIDYING,TERMINATED?)時,則線程應該中斷//?如果線程池狀態?<?STOP,?線程不應該中斷,如果中斷了(Thread.interrupted()?返回?true,并清除標志位),再次判斷線程池狀態(防止在清除標志位時執行了?shutdownNow()?這樣的方法),如果此時線程池為?STOP,執行線程中斷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?{//?如果執行到這只有兩種可能,一種是執行過程中異常中斷了,一種是隊列里沒有任務了,從這里可以看出線程沒有核心線程與非核心線程之分,哪個任務異常了或者正常退出了都會執行此方法,此方法會根據情況將線程數-1processWorkerExit(w,?completedAbruptly);} }

    來看看 processWorkerExit 方法是咋樣的

    private?void?processWorkerExit(Worker?w,?boolean?completedAbruptly)?{//?如果異常退出,cas?執行線程池減?1?操作if?(completedAbruptly)?decrementWorkerCount();final?ReentrantLock?mainLock?=?this.mainLock;mainLock.lock();try?{completedTaskCount?+=?w.completedTasks;//?加鎖確保線程安全地移除?worker?workers.remove(w);}?finally?{mainLock.unlock();}//?woker?既然異常退出,可能線程池狀態變了(如執行?shutdown?等),嘗試著關閉線程池tryTerminate();int?c?=?ctl.get();//??如果線程池處于?STOP?狀態,則如果?woker?是異常退出的,重新新增一個?woker,如果是正常退出的,在?wokerQueue?為非空的條件下,確保至少有一個線程在運行以執行?wokerQueue?中的任務????if?(runStateLessThan(c,?STOP))?{if?(!completedAbruptly)?{int?min?=?allowCoreThreadTimeOut???0?:?corePoolSize;if?(min?==?0?&&?!?workQueue.isEmpty())min?=?1;if?(workerCountOf(c)?>=?min)return;?//?replacement?not?needed}addWorker(null,?false);} }

    接下來我們分析 woker 從 workQueue 中取任務的方法 getTask

    private?Runnable?getTask()?{boolean?timedOut?=?false;?//?Did?the?last?poll()?time?out?for?(;;)?{int?c?=?ctl.get();int?rs?=?runStateOf(c);//?如果線程池狀態至少為?STOP?或者//?線程池狀態?==?SHUTDOWN?并且任務隊列是空的//?則減少線程數量,返回?null,這種情況下上文分析的?runWorker?會執行?processWorkerExit?從而讓獲取此?Task?的?woker?退出if?(rs?>=?SHUTDOWN?&&?(rs?>=?STOP?||?workQueue.isEmpty()))?{decrementWorkerCount();return?null;}int?wc?=?workerCountOf(c);//?如果?allowCoreThreadTimeOut?為?true,代表任何線程在?keepAliveTime?時間內處于?idle?狀態都會被回收,如果線程數大于?corePoolSize,本身在?keepAliveTime?時間內處于?idle?狀態就會被回收boolean?timed?=?allowCoreThreadTimeOut?||?wc?>?corePoolSize;//?worker?應該被回收的幾個條件,這個比較簡單,就此略過if?((wc?>?maximumPoolSize?||?(timed?&&?timedOut))&&?(wc?>?1?||?workQueue.isEmpty()))?{if?(compareAndDecrementWorkerCount(c))return?null;continue;}try?{//?阻塞獲取?task,如果在?keepAliveTime?時間內未獲取任務,說明超時了,此時?timedOut?為?trueRunnable?r?=?timed??workQueue.poll(keepAliveTime,?TimeUnit.NANOSECONDS)?:workQueue.take();if?(r?!=?null)return?r;timedOut?=?true;}?catch?(InterruptedException?retry)?{timedOut?=?false;}} }

    經過以上源碼剖析,相信我們對線程池的工作原理了解得八九不離十了,再來簡單過一下其他一些比較有用的方法,開頭我們提到線程池的監控問題,我們看一下可以監控哪些指標

    • int getCorePoolSize():獲取核心線程數。

    • int getLargestPoolSize():歷史峰值線程數。

    • int getMaximumPoolSize():最大線程數(線程池線程容量)。

    • int getActiveCount():當前活躍線程數

    • int getPoolSize():當前線程池中的線程總數

    • BlockingQueuegetQueue() 當前線程池的任務隊列,據此可以獲取積壓任務的總數,getQueue.size()

    監控思路也很簡單,開啟一個定時線程 ScheduledThreadPoolExecutor,定期對這些線程池指標進行采集,一般會采用一些開源工具如 Grafana + Prometheus + MicroMeter 來實現。

    如何實現核心線程池的預熱

    使用 ?prestartAllCoreThreads() 方法,這個方法會一次性創建 corePoolSize 個線程,無需等到提交任務時才創建,提交創建好線程的話,一有任務提交過來,這些線程就可以立即處理。

    如何實現動態調整線程池參數

    • setCorePoolSize(int corePoolSize) 調整核心線程池大小

    • setMaximumPoolSize(int maximumPoolSize)

    • setKeepAliveTime() 設置線程的存活時間

    解答開篇的問題

    其它問題基本都在源碼剖析環節回答了,這里簡單說下其他問題

    1、Tomcat 的線程池和 JDK 的線程池實現有啥區別, Dubbo 中有類似 Tomcat 的線程池實現嗎? Dubbo 中一個叫 EagerThreadPool 的東西,可以看看它的使用說明

    從注釋里可以看出,如果核心線程都處于 busy 狀態,如果有新的請求進來,EagerThreadPool 會選擇先創建線程,而不是將其放入任務隊列中,這樣可以更快地響應這些請求。

    Tomcat 實現也是與此類似的,只不過稍微有所不同,當 Tomcat 啟動時,會先創建 minSpareThreads 個線程,如果經過一段時間收到請求時這些線程都處于忙碌狀態,每次都會以 minSpareThreads 的步長創建線程,本質上也是為了更快地響應處理請求。具體的源碼可以看它的 ThreadPool 實現,這里就不展開了。

    2、我司網關 dubbo 調用線程池曾經出現過這樣的一個問題:壓測時接口可以正常返回,但接口 RT 很高,假設設置的核心線程大小為 500,最大線程為 800,緩沖隊列為 5000,你能從這個設置中發現出一些問題并對這些參數進行調優嗎?這個參數明顯能看出問題來,首先任務隊列設置過大,任務達到核心線程后,如果再有請求進來會先進入任務隊列,隊列滿了之后才創建線程,創建線程也是需要不少開銷的,所以我們后來把核心線程設置成了與最大線程一樣,并且調用 prestartAllCoreThreads() 來預熱核心線程,就不用等請求來時再創建線程了。

    線程池的幾個最佳實踐

    1、線程池執行的任務應該是互相獨立的,如果互相依賴的話,可能導致死鎖,比如下面這樣的代碼

    ExecutorService?pool?=?Executors.newSingleThreadExecutor(); pool.submit(()?->?{try?{String?qq=pool.submit(()->"QQ").get();System.out.println(qq);}?catch?(Exception?e)?{} });

    2、核心任務與非核心任務最好能用多個線程池隔離開來

    曾經我們業務上就出現這樣的一個故障:突然很多用戶反饋短信收不到了,排查才發現發短信是在一個線程池里,而另外的定時腳本也是用的這個線程池來執行任務,這個腳本一分鐘可能產生幾百上千條任務,導致發短信的方法在線程池里基本沒機會執行,后來我們用了兩個線程池把發短信和執行腳本隔離開來解決了問題。

    3、添加線程池監控,動態設置線程池

    如前文所述,線程池的各個參數很難一次性確定,既然難以確定,又要保證發現問題后及時解決,我們就需要為線程池增加監控,監控隊列大小,線程數量等,我們可以設置 3 分鐘內比如隊列任務一直都是滿了的話,就觸發告警,這樣可以提前預警,如果線上因為線程池參數設置不合理而觸發了降級等操作,可以通過動態設置線程池的方式來實時修改核心線程數,最大線程數等,將問題及時修復。

    總結

    本文詳細剖析了線程池的工作原理,相信大家對其工作機制應該有了較深入的了解,也對開頭的幾個問題有了較清楚的認識,本質上設置線程池的目的是為了利用有效的資源最大化性能,最小化風險,同時線程池的使用本質上是為了更好地為用戶服務,據此也不難明白 Tomcat, Dubbo 要另起爐灶來設置自己的線程池了。

    最后歡迎大家加我私人微信,一起討論,共同進步,拉你進讀者群,2020 難過,我們一起抱團取暖!

    巨人的肩膀

    • https://dzone.com/articles/how-much-memory-does-a-java-thread-take

    • https://segmentfault.com/a/1190000021047279

    • https://www.cnblogs.com/trust-freedom/p/6681948.html

    • 深入理解線程池 https://tinyurl.com/y675j928

    • 有的線程它死了,于是它變成一道面試題 https://mp.weixin.qq.com/s/wrTVGLDvhE-eb5lhygWEqQ

    • Java 并發編程實戰

    • Java線程池實現原理及其在美團業務中的實踐: https://mp.weixin.qq.com/s/baYuX8aCwQ9PP6k7TDl2Ww

    最后歡迎大家加我好友,拉你進技術交流群,群里有很多 BAT 的大咖,可以提問,互相交流,內推等,進群一起抱團取暖

    巨人的肩膀

    • https://dzone.com/articles/how-much-memory-does-a-java-thread-take

    • https://segmentfault.com/a/1190000021047279

    • https://www.cnblogs.com/trust-freedom/p/6681948.html

    • 深入理解線程池 https://tinyurl.com/y675j928

    • 有的線程它死了,于是它變成一道面試題 https://mp.weixin.qq.com/s/wrTVGLDvhE-eb5lhygWEqQ

    • Java 并發編程實戰

    • Java線程池實現原理及其在美團業務中的實踐: https://mp.weixin.qq.com/s/baYuX8aCwQ9PP6k7TDl2Ww

    • 線程池異常處理詳解,一文搞懂!https://www.cnblogs.com/ncy1/articles/11629933.html

    往期推薦

    23張圖!萬字詳解「鏈表」,從小白到大佬!


    面試官:你說說互斥鎖、自旋鎖、讀寫鎖、悲觀鎖、樂觀鎖的應用場景?


    25 張圖,1.4 w字!徹底搞懂分布式事務原理


    關注我,每天陪你進步一點點!

    總結

    以上是生活随笔為你收集整理的2万字,看完这篇才敢说自己真的懂线程池!的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    97人人添人澡人人爽超碰动图 | 日韩网站在线看片你懂的 | 91理论片午午伦夜理片久久 | 日本亚洲国产 | 国产一区二区在线看 | 九九九九九九精品任你躁 | 日本资源中文字幕在线 | 最新av在线网址 | 国产中文字幕视频在线观看 | 激情久久一区二区三区 | 成人av免费在线 | 日韩国产欧美在线播放 | 97电影在线看视频 | 日韩在线免费 | 九九免费观看全部免费视频 | 最新午夜 | 夜夜夜| 国产自在线 | 麻花豆传媒mv在线观看网站 | 97在线观看视频 | 黄色的视频 | av不卡免费在线观看 | 天天天天爱天天躁 | 一区二区视频免费在线观看 | 久久国产福利 | 久久精品这里精品 | mm1313亚洲精品国产 | 天天草网站 | 99视频国产在线 | 97热久久免费频精品99 | 久久精品一区二区三区四区 | 亚洲第一香蕉视频 | 精品国产伦一区二区三区观看方式 | 国产精品一区二区三区在线播放 | 精品国产电影一区二区 | 91色一区二区三区 | 日韩高清一二区 | 欧美最爽乱淫视频播放 | 午夜影院三级 | 国产精品扒开做爽爽的视频 | 亚洲综合一区二区精品导航 | www.久久爱.cn| 99看视频在线观看 | 五月激情片 | 人人爽人人插 | 日韩精品一区二区在线观看 | 日韩欧美一区二区三区在线观看 | 免费在线观看国产精品 | 亚洲另类视频在线观看 | 91视频啪| 91成人网在线 | 九九视频在线播放 | 成人午夜毛片 | 黄色片网站av | 在线观看午夜av | 麻豆国产精品va在线观看不卡 | 成年人黄色在线观看 | 国产精品地址 | 欧美激情操 | 性色av免费在线观看 | 99免费精品视频 | 去看片 | 精品国产aⅴ一区二区三区 在线直播av | 激情综合色综合久久 | 视频成人免费 | 99精品在线观看视频 | 97精品国产97久久久久久免费 | 国产亚洲午夜高清国产拍精品 | 亚洲午夜剧场 | 日韩av三区 | 国产精品xxxx18a99 | 最近免费中文字幕大全高清10 | 国产精品va在线观看入 | 五月婷婷丁香色 | 最新av电影网站 | 狠狠狠狠狠色综合 | 亚洲国产999 | 国产日韩一区在线 | 中文字幕刺激在线 | 国产精品va在线观看入 | 国产精品一区二区三区在线 | 久热久草在线 | 综合网av | 免费情趣视频 | 国产三级久久久 | 在线中文字幕电影 | 亚洲国产色一区 | 丁香婷婷综合五月 | 亚洲精品国产日韩 | 成人黄色电影在线播放 | 91网免费看 | 欧美高清视频不卡网 | 国产乱码精品一区二区三区介绍 | 色综合久久中文字幕综合网 | 992tv在线成人免费观看 | 亚洲综合激情五月 | 国产无遮挡又黄又爽馒头漫画 | 午夜少妇一区二区三区 | 在线观看一二三区 | av中文天堂在线 | 久久高清片 | 91亚洲精品国产 | 亚洲国产精品人久久电影 | 国产精国产精品 | 免费看的国产视频网站 | 国产高清第一页 | 国产69精品久久久久99 | 激情偷乱人伦小说视频在线观看 | 国产麻豆果冻传媒在线观看 | 一区二区三区视频网站 | 色综合天天狠天天透天天伊人 | 国产一级电影免费观看 | 免费观看9x视频网站在线观看 | 亚洲成人黄| 久久久综合 | 国产精品毛片久久久久久久久久99999999 | 激情综合网婷婷 | 日韩欧美高清不卡 | 久亚洲精品 | 国产成人一区二区精品非洲 | 天天操网| 久久成人久久 | 精品视频 | 国色天香在线观看 | 激情视频免费在线 | 五月婷网 | 精品久久国产精品 | 一区 在线 影院 | 亚州av成人 | 精品五月天 | 精品国产91亚洲一区二区三区www | 中文字幕日韩国产 | 99久久精品国产免费看不卡 | 国产成人高清av | 91视频免费看网站 | 丁香五婷 | 中文字幕不卡在线88 | 国产成人久久 | 亚洲精品乱码久久久一二三 | 欧美91精品 | 中文超碰字幕 | 日韩在线视频网 | 在线va网站| 亚洲美女视频在线 | 手机成人免费视频 | 天天干天天拍天天操天天拍 | 亚洲麻豆精品 | 久久久久久久久亚洲精品 | 午夜美女福利直播 | 1024在线看片 | 婷婷香蕉 | 日韩一二区在线观看 | 激情视频在线观看网址 | 日韩精品久久一区二区三区 | 一区二区电影在线观看 | 日韩精品视频在线观看免费 | 男女激情片在线观看 | 天天爽天天摸 | 黄色在线免费观看网站 | 激情五月视频 | 精品视频在线视频 | 久热只有精品 | 粉嫩aⅴ一区二区三区 | 免费看污黄网站 | 精品亚洲视频在线 | 日韩三级av | 在线观看黄色国产 | 午夜av在线电影 | 国产精品99精品久久免费 | 国产成人精品一区二区在线观看 | 五月天激情婷婷 | 色综合咪咪久久网 | 日韩a级黄色 | 波多野结衣久久精品 | 成人av直播| 久久理论电影 | 免费看国产精品 | 五月天激情视频 | 国产精品久久99精品毛片三a | 中文字幕在线观看视频一区二区三区 | 91精品国产成 | 最新国产福利 | 玖玖色在线观看 | 国产午夜三级一二三区 | 天天爽天天爽天天爽 | 欧美在线aaa| 国内三级在线 | 色婷婷www | 久久中文字幕在线视频 | 人人爽人人干 | 日韩区欠美精品av视频 | 麻豆国产视频 | 在线中文字母电影观看 | 99九九热只有国产精品 | 综合激情网... | 最近最新中文字幕视频 | 亚洲狠狠干 | 中文字幕久久精品亚洲乱码 | 成人av网站在线观看 | 国产中文字幕一区二区 | 欧美色图另类 | 欧美日本啪啪无遮挡网站 | 天天干天天搞天天射 | 色偷偷88欧美精品久久久 | 久草成人在线 | 亚洲观看黄色网 | 成人毛片一区二区三区 | 亚洲精品短视频 | 国产精品久久久久久久午夜片 | 国产日韩欧美在线看 | 波多野结衣在线播放一区 | 99亚洲国产精品 | 亚洲一区二区三区四区精品 | 91精品久久久久久久久久入口 | 日韩av网页 | 欧美激情第八页 | 97精品国产手机 | 国产精品久久久久久69 | 在线播放第一页 | 天天躁日日躁狠狠躁 | 久久1电影院 | 97在线看 | 国产成人精品一区二区三区免费 | 成人啪啪18免费游戏链接 | 美州a亚洲一视本频v色道 | 亚洲 综合 激情 | 日本精品二区 | 99综合影院在线 | 欧美日韩在线视频观看 | 中文在线免费视频 | 成片视频在线观看 | 欧美日韩在线视频观看 | 久久麻豆视频 | 亚洲色五月 | 亚洲精品成人网 | 欧美日韩高清 | 日日干天天操 | 99久久精品午夜一区二区小说 | 在线亚洲人成电影网站色www | 国产免码va在线观看免费 | 久久久九色精品国产一区二区三区 | 成年人在线 | 成人欧美一区二区三区在线观看 | 国产日韩一区在线 | 欧美日韩视频在线观看免费 | 国产色综合天天综合网 | 狠狠狠色丁香综合久久天下网 | 一区二区不卡视频在线观看 | 日韩激情中文字幕 | 91色综合 | 91视频免费观看 | 美女在线观看网站 | 九九久久久久99精品 | 五月婷婷在线播放 | 狠狠色伊人亚洲综合网站色 | 精品国产乱码久久久久久三级人 | 久久综合狠狠综合久久狠狠色综合 | 午夜丰满寂寞少妇精品 | 黄色片网站大全 | 免费a一级| 超碰在线免费福利 | 久久久久一区二区三区四区 | 天堂久久电影网 | 国产一级一级国产 | 一本一道久久a久久精品 | 免费av黄色 | 婷婷丁香社区 | 日韩xxxxxxxxx| 丁香六月婷婷开心 | 碰超在线 | 亚洲精品久久视频 | 亚洲精品国产精品久久99 | 色爽网站 | 亚洲精品视频在线播放 | 婷婷中文字幕 | 欧美日韩高清一区二区三区 | 国产一级视频在线免费观看 | 91麻豆精品国产91久久久无限制版 | 你操综合 | 国产精品18久久久久久久久久久久 | 亚洲成人蜜桃 | 国产高清绿奴videos | 国产精品淫 | 国产亚洲欧美在线视频 | 精品国产欧美一区二区 | 欧美a免费 | 黄色毛片在线观看 | 一区免费视频 | 西西人体www444 | 美女网站在线免费观看 | 国产一区二区在线免费观看 | 黄色av电影在线 | 国产精品久久久久久久久大全 | 欧美人交a欧美精品 | 深夜国产在线 | 国产精品久久视频 | 天天射夜夜爽 | 国产精品自产拍在线观看中文 | www.狠狠操 | 国产精品视频观看 | 国产精品一区二区在线观看 | 中文字幕亚洲高清 | www四虎影院| 狠狠狠狠狠干 | 国产高清福利在线 | 国产精品免费在线播放 | 国产精品黄| 最近中文国产在线视频 | 又湿又紧又大又爽a视频国产 | 狠狠狠色狠狠色综合 | 亚洲精品免费在线观看视频 | 亚洲精品美女视频 | 97精品国产97久久久久久免费 | 911精品美国片911久久久 | 最近日本mv字幕免费观看 | 国产精品毛片一区二区三区 | 婷婷九九 | 日韩有码欧美 | 亚洲国产av精品毛片鲁大师 | 欧美aⅴ在线观看 | 精品久久国产精品 | 中文免费 | 欧美日韩大片在线观看 | 成人羞羞视频在线观看免费 | 99高清视频有精品视频 | 日韩网站在线免费观看 | 91免费视频网站在线观看 | 中文字幕在线影院 | 国产亚洲婷婷免费 | 国产精品久久久久久爽爽爽 | 2021av在线 | 黄色大片国产 | 国产成人精品亚洲 | 亚洲精品视频在线观看免费 | 国产午夜精品视频 | 欧美国产亚洲精品久久久8v | 九九在线播放 | 天天鲁天天干天天射 | 91三级视频 | 在线观看免费视频 | 日韩电影在线观看一区二区 | 免费亚洲精品视频 | 中文字幕第一页在线播放 | 天天爱天天操天天射 | 黄色av电影 | 日本成人黄色片 | 91私密保健| 福利视频精品 | 伊人五月在线 | 国产福利一区在线观看 | 天天视频色版 | 成年人在线观看视频免费 | 激情综合婷婷 | www.久久99| 在线观看免费成人av | 久久伦理网 | 午夜的福利 | 日韩欧美xx | 91精品国产福利在线观看 | 国产精品第三页 | 成年人黄色大片在线 | 久久综合狠狠综合 | 五月天丁香综合 | 久久精品一区二区三 | 999成人网 | 国产精品久久综合 | 豆豆色资源网xfplay | 国产一线二线三线在线观看 | 国产原创在线观看 | 午夜三级福利 | 成年人免费在线观看网站 | 奇米先锋 | 91在线看片| 超碰97av在线| 一区二区精品在线 | 天天干天天射天天操 | 麻豆视屏| 亚洲精品国产精品国自产观看浪潮 | 91天堂影院 | 欧美激情精品久久久 | 五月天视频网 | 亚洲激情在线 | 黄色国产区 | 精品国产一区二区三区噜噜噜 | 亚洲激情综合 | 午夜精品久久一牛影视 | 久久久久久久久久久成人 | 人人插人人看 | 久久久久久欧美二区电影网 | 一级淫片在线观看 | 91在线视频播放 | 色橹橹欧美在线观看视频高清 | 在线免费观看视频一区二区三区 | 在线观看国产www | 久草精品视频在线播放 | 视频三区在线 | 99久久国产免费,99久久国产免费大片 | 麻豆精品视频在线观看免费 | 最近中文字幕国语免费高清6 | 国产精品去看片 | 免费a级毛片在线看 | 日韩在线电影一区二区 | 国产高清免费在线观看 | 五月婷网站 | 三级视频日韩 | 日本久久免费视频 | 国产精品videoxxxx | 91在线产啪 | 亚州精品成人 | 国产伦理久久精品久久久久_ | 国产精品久久久久一区二区 | 久久精品一区二区三区国产主播 | 色老板在线 | 欧美性性网 | 国产亚洲成人网 | 在线观看免费观看在线91 | 亚洲 精品在线视频 | 亚洲欧美精品一区二区 | 中文字幕在线观看网站 | 黄色性av | 香蕉视频在线看 | 中文字幕视频三区 | 久久五月精品 | 欧美成人精品欧美一级乱 | 精品日韩在线一区 | 天天色天天干天天色 | 婷婷久久精品 | 在线播放视频一区 | 国产精品破处视频 | 黄色a视频免费 | 日韩动态视频 | 欧美精品在线观看免费 | 一区二区在线电影 | 亚洲精品一区二区在线观看 | 国产特级毛片aaaaaa高清 | 国产福利久久 | 欧美日韩国产综合网 | 日本黄区免费视频观看 | 在线播放 一区 | 日韩高清在线一区二区 | 狠狠色伊人亚洲综合网站野外 | 97av在线 | 在线免费av观看 | 国产精品美女www爽爽爽视频 | 日韩视频在线不卡 | 国产精品免费视频一区二区 | 69亚洲视频 | 激情久久小说 | 美女免费网站 | 国产亚洲免费观看 | 婷婷在线不卡 | 亚洲h在线播放在线观看h | 欧美一级黄色视屏 | 婷婷综合亚洲 | 国内精品福利视频 | 亚洲精品啊啊啊 | 丁香婷婷成人 | 成人免费看片网址 | 欧美亚洲久久 | 国产精选在线观看 | 久久精品爱爱视频 | 亚洲成人黄色网址 | 久草在线手机视频 | 免费看色的网站 | 偷拍区另类综合在线 | 粉嫩av一区二区三区四区 | 日日夜夜天天 | 99久久这里只有精品 | 欧美一级高清片 | 亚洲黄色高清 | 日三级在线 | 国产色网| 久久er99热精品一区二区三区 | 久久免费高清视频 | 国产精品美女久久久久久久 | 天天做天天看 | 四虎最新入口 | www.日韩免费 | 青青草视频精品 | 国产亚洲精品久久久久久电影 | 精品99视频 | 又湿又紧又大又爽a视频国产 | 最新国产福利 | 黄p在线播放 | 91中文在线视频 | 久久视奸 | 综合激情网... | 成人精品视频久久久久 | 玖玖国产精品视频 | 五月婷婷一区二区三区 | 午夜av激情 | 亚洲综合视频在线播放 | 国产人成一区二区三区影院 | av在线看片 | 国产精品久久99综合免费观看尤物 | av免费网页| 亚州av网站| 成人久久久久久久久久 | 国产一区二区三区高清播放 | 日韩av男人的天堂 | 国产精品99久久久久久小说 | 国产亚洲精品综合一区91 | 91视频 - 114av | 日韩三级视频在线观看 | 国产69精品久久99的直播节目 | 91天堂素人约啪 | 日韩一区二区三区在线观看 | 人人要人人澡人人爽人人dvd | 免费亚洲婷婷 | 亚洲精选99 | 在线播放国产精品 | 丁香婷婷综合激情五月色 | 在线观av | 激情五月婷婷激情 | 在线免费av电影 | 国产精品视频免费看 | 免费久久视频 | 美女久久99 | 久久99国产综合精品 | 97免费| 狠狠色噜噜狠狠狠合久 | 亚洲影音先锋 | 亚洲综合在线观看视频 | 97看片| 日韩另类在线 | 国产精品久一 | 青青河边草免费直播 | 中文字幕免费高清av | 日p视频 | 91在线亚洲| 国产精品久久网 | 亚洲成人精品久久 | 五月婷婷免费 | 国产一区在线视频观看 | 国产精品免费久久久久久久久久中文 | 特级毛片爽www免费版 | 人人玩人人添人人澡超碰 | 欧美午夜剧场 | 欧美久久久久久久久久久 | 99精品福利视频 | 五月婷婷色 | 国产午夜激情视频 | 一级性av| 亚洲精品免费看 | 亚洲理论片在线观看 | 在线免费观看视频一区二区三区 | 成年人视频在线观看免费 | 国产精品theporn | 国产成人精品一区在线 | 中文字幕色婷婷在线视频 | 蜜臀久久99精品久久久久久网站 | 日韩黄色免费电影 | 人人舔人人爽 | 99久免费精品视频在线观看 | 三级黄在线 | 国产亚洲精品久久久久久移动网络 | 色婷婷激情电影 | 成人福利在线 | 国产不卡一二三区 | 亚洲国产经典视频 | 久久久精品综合 | 91精品国产自产在线观看 | 国产在线超碰 | 国产91免费在线观看 | 韩国av免费看 | 日韩极品在线 | 色婷婷国产精品一区在线观看 | 色婷婷综合久久久中文字幕 | 99精彩视频在线观看免费 | 日韩专区在线观看 | 99精品国产在热久久 | h视频日本| japanesefreesex中国少妇 | 国产一区免费在线观看 | 国产美女无遮挡永久免费 | 国产91探花 | 在线观看片 | 人交video另类hd | 黄色一级大片在线观看 | 91精品国产成人观看 | 欧美在线aa | 在线你懂的视频 | 特黄一级毛片 | 久久99精品国产麻豆宅宅 | 国产成人综合图片 | www操操| 日韩欧美中文 | 精品在线观看国产 | 日本久久高清视频 | 米奇四色影视 | 视频一区二区三区视频 | 国内视频1区 | 日p视频在线观看 | a色视频 | 在线观看免费高清视频大全追剧 | 中国一级片免费看 | 婷婷丁香激情五月 | 亚洲精品国偷自产在线91正片 | 亚洲精品国产免费 | 免费看国产视频 | 一区二区视频在线播放 | 在线成人免费电影 | 2018亚洲男人天堂 | 日本久久91| 久久激情日本aⅴ | 91色欧美| 久久精品久久久久电影 | 人人超碰免费 | 手机看片福利 | 精品一区二区在线播放 | 在线导航福利 | 三级黄免费看 | 亚洲欧美视频在线播放 | 狠狠的干 | 国产一线二线三线在线观看 | 国产精品热视频 | 日韩欧美在线观看 | 国产精品白虎 | 中文字幕av一区二区三区四区 | 色综合久久久久综合99 | 五月婷影院| 色综合久久五月天 | 欧美一区二区免费在线观看 | 国产一及片 | 亚洲桃花综合 | 国产一二三四在线视频 | 91精品综合在线观看 | 探花视频在线观看免费 | 亚洲午夜精品久久久久久久久 | 偷拍久久久 | 毛片网在线播放 | 在线视频福利 | 成人a级黄色片 | 人人干人人草 | 色五丁香 | 美女国产精品 | 亚洲年轻女教师毛茸茸 | 亚洲狠狠婷婷 | 欧美日韩视频网站 | 欧美一级大片在线观看 | 成年人网站免费在线观看 | 99色在线观看视频 | 激情网在线视频 | 丁香一区二区 | 91香蕉视频色版 | a级免费观看 | 中文字幕一区二区三区在线视频 | 一级黄色网址 | 精品久操| 成人四虎影院 | www.久久久久 | 免费在线观看午夜视频 | 天天射天天拍 | 91精品小视频 | 成人黄色片免费看 | 久久五月情影视 | 欧美日韩xxxxx| 婷婷丁香激情综合 | 久久久久在线 | 香蕉视频久久 | 99色99| 欧美aaa大片 | 麻豆一精品传二传媒短视频 | 婷婷六月色 | 99精品视频在线观看播放 | 国产成人av一区二区三区在线观看 | 国产爽妇网| 亚洲成人免费 | 日日碰狠狠添天天爽超碰97久久 | 久久五月天色综合 | 精品国产伦一区二区三区观看体验 | 日韩伦理片一区二区三区 | 在线观看福利网站 | 国产精品久久久久9999吃药 | 国产在线观看高清视频 | 制服丝袜天堂 | 日韩欧美在线播放 | 天天干天天弄 | 五月天国产精品 | 99热高清 | 69久久99精品久久久久婷婷 | 精品久久久久久久 | 日韩网站一区二区 | 亚洲中字幕 | 久久天天躁狠狠躁亚洲综合公司 | 成人一级片视频 | 日本特黄一级片 | 国产精品美女久久久久久久 | 国产在线中文字幕 | av网站在线免费观看 | 丁香婷婷基地 | 午夜久久久久久久 | 99精品国产一区二区三区麻豆 | 亚洲aⅴ久久精品 | 国产精品电影一区二区 | 色婷婷国产精品一区在线观看 | 青青河边草免费观看完整版高清 | 欧美日韩中字 | 色婷婷丁香 | 在线观看香蕉视频 | 91av在线播放视频 | 欧美日韩首页 | 欧美孕妇与黑人孕交 | 69久久99精品久久久久婷婷 | 中文字幕 国产视频 | 成人午夜影院在线观看 | 欧美色图30p | 欧美在线久久 | 成人丝袜| 手机看国产毛片 | 国内精品久久久久久久久久久久 | 久久影视中文字幕 | 亚洲va欧洲va国产va不卡 | 2019精品手机国产品在线 | 91精品少妇偷拍99 | 成人理论电影 | 亚洲电影免费 | 91久久国产综合精品女同国语 | 亚洲精品视频网址 | 国产精品久久久久久久毛片 | 777奇米四色 | 国产一级在线播放 | 五月婷婷综合激情网 | 精品久久久久久久久久久院品网 | 婷婷色网视频在线播放 | 精品视频不卡 | 国产一级电影 | 日韩天堂网 | 五月开心激情网 | 国产对白av | 波多野结衣电影一区 | 亚洲 欧美 变态 国产 另类 | 日韩国产精品毛片 | 精品国产欧美一区二区 | 久人人 | 99久久日韩精品免费热麻豆美女 | 成人一级片免费看 | 黄色大片日本 | 中文字幕日韩国产 | 国产午夜精品视频 | 亚洲在线精品 | 欧美精品在线观看免费 | 亚洲区视频在线 | 国产xx视频 | 天天天天天干 | 亚洲精品乱码久久 | 成人av在线直播 | 97在线免费视频观看 | 日韩在线网址 | 久久99久久99精品免费看小说 | 啪嗒啪嗒免费观看完整版 | 国产美女黄网站免费 | 久久精品爱爱视频 | 五月婷婷激情五月 | 久久久久亚洲天堂 | 91成人区| 国产日产高清dvd碟片 | 国产一级在线播放 | 99久久久久国产精品免费 | 国产视频中文字幕在线观看 | 一区二区三区在线视频111 | 天天操天操 | 久久视频免费在线观看 | 国产五十路毛片 | 国产精品美女999 | 黄色精品一区 | 久久人人爽人人爽人人片av软件 | 九九九视频在线 | 久草在线在线精品观看 | 国产一区二区在线视频观看 | 欧美韩国日本在线 | 国产欧美日韩一区 | 国产 精品 资源 | 国产中文字幕国产 | 成人国产精品一区 | 91少妇精拍在线播放 | 天天射天天爱天天干 | 婷婷综合成人 | 五月婷婷一区二区三区 | 欧美黑人xxxx猛性大交 | 国产精品九九久久99视频 | 久久久久久久久久久久久久免费看 | 日韩欧美91 | 美女av免费看 | 在线视频91 | 麻豆视频在线观看免费 | 国产精品完整版 | 九九久久精品视频 | 久久国产精品免费一区二区三区 | 黄色片毛片 | 色播99 | 免费看麻豆 | av在线播放中文字幕 | 激情综合五月天 | 国产中文字幕第一页 | 欧美色图亚洲图片 | 亚洲精品字幕在线 | 日本特黄特色aaa大片免费 | 免费观看av网站 | 亚洲国产福利视频 | 日韩电影精品一区 | 亚洲综合色丁香婷婷六月图片 | av一级久久 | 色就色,综合激情 | 日韩一区二区三区在线观看 | 91激情视频在线观看 | 精品国产欧美一区二区 | 黄色av网站在线免费观看 | 午夜精品婷婷 | 国产视频一二三 | 一区二区中文字幕在线观看 | 国产精品a成v人在线播放 | 久久精品久久精品久久精品 | 麻豆高清免费国产一区 | 久久免费播放视频 | 福利一区二区在线 | 国产精品免费观看久久 | 婷婷伊人五月 | 婷婷九月激情 | 少妇高潮流白浆在线观看 | 亚洲国产欧美一区二区三区丁香婷 | 97日日碰人人模人人澡分享吧 | 亚洲不卡在线 | 欧美怡红院视频 | 久久久久久麻豆 | 一区二区三区日韩视频在线观看 | 美女在线观看网站 | 91色影院| 91爱爱视频 | 91在线你懂的| 国产精品久久久久久99 | 久久精品中文字幕少妇 | 97精品国产97久久久久久免费 | 国产精品亚洲a | 人人澡超碰碰 | 狠狠操狠狠操 | 国产中文字幕在线 | 久久久高清一区二区三区 | 97人人模人人爽人人喊网 | 91黄视频在线| 久久久久久久久久久久av | 中文在线a√在线 | 美女激情影院 | 国产裸体视频网站 | 欧美一级片在线播放 | 久草视频99 | 精品视频专区 | 91人人网| 激情开心站 | 国产精品一区二区你懂的 | 精品国产99国产精品 | 综合五月 | 91亚洲网站| 五月婷婷av| 国产精品久久久久久久久久 | 成人中文字幕+乱码+中文字幕 | 成人在线免费视频 | 国产精品扒开做爽爽的视频 | 91手机电视 | 亚洲精品av在线 | 久热色超碰 | 粉嫩av一区二区三区免费 | 久久麻豆精品 | 久久久免费国产 | 国产一级精品视频 | 天天操天天射天天爱 | 欧美91在线| 婷婷网在线 | 99久久精品国产免费看不卡 | 国内精品久久久久久久97牛牛 | 色网站在线观看 | 午夜av一区二区三区 | 最近免费中文视频 | 国产成人黄色网址 | 一级免费看 | 天天亚洲| 亚洲精品免费在线视频 | 国产一级免费在线观看 | 亚洲精品视频免费在线 | 91视频a | 亚洲97在线 | 日韩免费一级电影 | 免费黄av| 丝袜美腿在线播放 | 激情久久综合 | 中国精品少妇 | 激情久久网 | 黄色软件视频大全免费下载 | 久久婷婷精品 | 国内精品久久久久影院日本资源 | 成年人免费在线观看网站 | 91男人影院 | 国产精品24小时在线观看 | 国产精品黄色影片导航在线观看 | 久久亚洲日本 | 久久久免费少妇 | 亚洲视频一区二区三区在线观看 | 免费一级日韩欧美性大片 | 亚洲成人精品在线 | 亚洲天堂毛片 | 国产亚洲一级高清 | 国产原厂视频在线观看 | 99久久999久久久精玫瑰 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 免费成人在线观看视频 | 超碰99人人 | 国产精品一区二区av日韩在线 | 亚洲午夜av久久乱码 | 国产精品 中文在线 | 日本电影黄色 | 国产99久久久国产精品 | 蜜桃麻豆www久久囤产精品 | 色综合久久久久久久久五月 | 久草网视频在线观看 | 99精品国产免费久久 | 人人插人人爱 | 国产中文字幕在线播放 | 亚洲精品在线视频网站 | 成人av一区二区兰花在线播放 | 国产视频午夜 | 一区二区三区精品久久久 | 久草在线综合 | 精品久久99| 亚洲影院天堂 | 91精品一区在线观看 | 亚洲国产视频直播 | 日日草视频 | 欧美国产日韩一区二区 | 国产97视频在线 | 黄色av免费看 | 国产精品美女在线 | 国内精品久久久久 | 久操久| 久久精彩视频 | 五月在线视频 | 日韩av看片| 三级小视频在线观看 | 精品人妖videos欧美人妖 | 久久久久久久久久久久国产精品 | 成人理论电影 | 韩国av免费看 | 久久69av| 午夜视频在线观看一区二区三区 | 久久综合毛片 | 中文av网 | 超碰在线色 | 中文在线免费看视频 | 伊人久久五月天 | 欧美精品一区二区免费 | av电影在线观看完整版一区二区 | 久久久香蕉视频 | 欧美色图亚洲图片 | 国产高清久久久 | 国内精品久久久久久久 | 国产精品国产三级国产aⅴ入口 | 久久激情婷婷 | 国产一区二区久久久久 | 99精品视频一区二区 | 天天色综合三 | 在线观看国产麻豆 | 国产露脸91国语对白 | 成人h视频在线播放 | 久久久蜜桃| 中文字幕电影网 | 免费的黄色的网站 | 在线免费观看黄色大片 | 美女国内精品自产拍在线播放 | 97色综合| 国产伦理一区二区 | 91夫妻视频 | 亚洲国产一二三 | 不卡中文字幕av | 精精国产xxxx视频在线播放 | 国产精品成人一区二区 | 激情丁香久久 | 国产片免费在线观看视频 | 免费高清在线一区 | 日本三级人妇 | 久久久国产精品网站 | 日韩精品中文字幕av | 国产精品视频内 | 日韩网站在线播放 | 9在线观看免费高清完整 | 成人va视频| 精品国产久 | 最近日本字幕mv免费观看在线 | 激情影音先锋 | av电影免费在线播放 | www.天天草 | 日韩黄视频 | 视频在线观看一区 | 一级欧美黄 | 精品国产1区2区3区 国产欧美精品在线观看 | 你操综合 | 天天色天天骑天天射 | 这里只有精品视频在线观看 | 天天艹天天 | 色婷婷免费 |