欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java虛擬機多線程進階篇總結(jié)

 更新時間:2019年06月07日 16:00:22   投稿:laozhang  
在本篇內(nèi)容里小編給大家整理了關(guān)于java虛擬機多線程進階篇的相關(guān)知識點內(nèi)容,有興趣的朋友們跟著參考下。

1.線程池基本參數(shù)

以Executors.newFixedThreadPool()這種創(chuàng)建方式為例:

大家想象,假如你創(chuàng)建一個線程池,你想這個池子有些什么參數(shù)呢?首先這個池子必須要有一個最大值;然后還希望這個池子的線程數(shù)量有一個警戒線,到了這個警戒線的位置說明線程池暫時已經(jīng)滿了,如果這個時候還有人過來拿線程,我們就要把這些人抓起來扔到一個地方去讓他們排隊,告訴他們:請稍等,等我們的線程有空閑的時候再來處理你的事;再然后假如人排隊的地方都滿了,瑪?shù)?,好多人,于是線程池就想辦法東拼西湊又多搞出來了幾個線程去處理了;最后,假如那搞出來的這幾個線程還是不夠用,并且排隊的地方總是滿的,于是線程池生氣了,就這么多人可以了,如果還有人過來的趕緊讓它滾蛋;

這里我們需要知道幾個東西:

1這里的警戒線叫做核心線程池大小(corePoolSize);

2.最大值還是叫做線程池線程最大數(shù)量(maximumPoolSize)

3.排隊的地方叫做隊列(BlockingQueue<Runnable> ),這個隊列用于保存我們的線程要做的任務(wù),這個隊列有好幾種類型,我們后面會分析的;

4.還有一個參數(shù)是keepAliveTime:線程存活時間,意思就是當(dāng)池中總共的線程大于核心線程池數(shù)目,那就關(guān)閉池子中的空閑線程,要保證線程總數(shù)維持在核心線程池數(shù)目或者之下;

現(xiàn)在我們來理一下邏輯:

池中當(dāng)前線程數(shù)量 <= 核心線程池大小:線程池直接創(chuàng)建線程處理

池中當(dāng)前線程數(shù)量 > 核心線程池數(shù)量:將多余的任務(wù)放進隊列

隊列滿了,還有任務(wù)過來,線程池繼續(xù)創(chuàng)建線程,直到到達線程池最大數(shù)量

還有任務(wù)過來,這里會有一個飽和策略,默認(rèn)是直接丟棄繼續(xù)過來的任務(wù)

2.線程池種類

我們上一節(jié)使用的線程池如下所示:

ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new RunnableImpl("玩游戲"));

我們是通過Executors這個類的靜態(tài)方法創(chuàng)建的一個線程池,于是進入這個類我們看看這個類還有沒有創(chuàng)建其他種類線程池的方法,居然還真有。。。

我們先簡單說說這四種分別是干嘛用的;

newFixedThreadPool(int):這個線程池就是上面說的那種方式,也是我們重點要看源碼的線程池;

newSingThreadExecutor():這個不能說是線程池了,因為里面這里面只有一個線程,而且自帶一個隊列,只要有任務(wù)來了就會把任務(wù)保存到隊列中,然后這個線程就慢慢的一個一個執(zhí)行。

newCachedThreadPool():無限線程的線程池

newScheduledThreadPool(int):一個定時的線程池,可以讓線程池中的線程延遲指定時間再執(zhí)行任務(wù);

3.Executors繼承結(jié)構(gòu)

我們可以看到實際上實例化的是一個ThreadPoolExecutor對象,這個對象作用是用線程去處理傳進去的任務(wù):

我們看一下這個繼承結(jié)構(gòu),

Executor接口:只是定義了execute();這個方法,等待子類去實現(xiàn);

ExecutorService接口:繼承Execute接口,并又聲明了shutdown()方法和submit()方法,等待子類去實現(xiàn)

AbstractExecutorService抽象類:初步實現(xiàn)了submit()方法,但是內(nèi)部調(diào)用的execute()方法去執(zhí)行任務(wù)

ThreadPoolExecutor類:這個類是實現(xiàn)了很多的方法,將shutdown()和execute()方法都給實現(xiàn)了;

4.看看execute()方法源碼

下面我們主要就是看看execute()方法的內(nèi)部是怎么實現(xiàn)的,知道了這個的實現(xiàn)原理也就差不多了 

 public void execute(Runnable command) {
  if (command == null)
   throw new NullPointerException();
   int c = ctl.get();
    
//workCountOf(c)表示當(dāng)前線程池中線程的數(shù)量;這里進行一個判斷,當(dāng)線程池中線程數(shù)目小于核心池子數(shù)目時,
  就調(diào)用addWorker()方法將我們的任務(wù)添加進去,等下可以看到addWorker()方法內(nèi)部其實就是創(chuàng)建線程并處理請求,
  就類似new Thread(xxx).start()這種方式
    if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); }

//如果當(dāng)前線程數(shù)目大于核心線程并且任務(wù)放入一個隊列成功,內(nèi)部還會再次進行線程池狀態(tài)判斷,這里的&&用得比較精髓(短路作用),好好體會一下,
  假如不是運行狀態(tài)那就會執(zhí)行remove方法 刪除隊列中的任務(wù),如果是運行狀態(tài)直接進入else if,這里的目的是線程池中已經(jīng)關(guān)閉了,我們添加一個null任務(wù)
  表示線程池不再處理任務(wù)
    if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); }

//能執(zhí)行到這里,說明上面兩個if中的條件都不滿足,條件應(yīng)該是:當(dāng)前線程大于核心線程,并且向隊列中添加任務(wù)失敗,換句說說就是對列已經(jīng)滿了,裝不下這么多任務(wù)
  于是我們reject()方法內(nèi)部就是對這些多余的任務(wù)進行處理的一些策略,默認(rèn)就是直接丟棄 else if (!addWorker(command, false)) reject(command); }

對于面這三種情況的判斷還是很清楚的,我們忽略很多細(xì)節(jié),因為我們的目的是要對整個邏輯有個大概的了解,而不是去完全消化這些源碼,這很不現(xiàn)實,要想理解透徹只能慢慢的去研究...

我們來看看最重要的addWorker()這個方法,這個方法就是線程池將我們傳進來的new Runnable(xxx)進行處理,其實內(nèi)部就是用new Thread(xxxx).start()處理,只是出于線程池中會進行很多的條件判斷以及將Runnable()做進一步的封裝,我們了解就好,代碼如下:

private boolean addWorker(Runnable firstTask, boolean core) {
  //這里刪除很多的條件判斷的代碼
    ..........
    
     boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { final ReentrantLock mainLock = this.mainLock; //注意下面這兩行,其實就是將我們傳進來的Runnable()進行封裝成Worker,在Worker構(gòu)造器里面會new Thread()并且保存起來
       這樣做的一個好處就是直接將一個線程和一個Runnable進行綁定,我們隨時可以從Worker中獲取線程然后調(diào)用start()方法就ok了
       w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { mainLock.lock(); try { //此處刪除一些             ......... if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException();
              //由于會有很多個Worker,于是我們會創(chuàng)建HashSet<Worker> workers = new HashSet<Worker>(),用于保存所有的worker,后續(xù)直接遍歷處理很方便
              而且我們所說的線程池的本質(zhì)就是這個workers,也就是一個HashSet
              workers.add(w);
              int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); }
          //下面這個if語句中就是一個無限循環(huán)的去執(zhí)行線程的start()方法 if (workerAdded) { t.start(); workerStarted = true; } } }return workerStarted; }

說出來你可能不信,我有點沒看懂這里,因為最后的那個start()方法總感覺有點問題,但是說不上來,你們覺得這個start()方法之后,CPU來運行這個線程會執(zhí)行哪個run()方法?是我們傳進去的類的run()方法?還是worker的run()方法呢?

我們看看下面這兩行代碼,Worker構(gòu)造器中的新建線程的代碼就不截圖了,我們把下面這幾行代碼變化一下:

Worker w = new Worker(firstTask);
final Thread t = w.thread;
........
t.start()

變化后:  

Worker w = new Worker(firstTask);//firstTask是我們傳進去的實現(xiàn)了Runnable接口的類,但是Worker也實現(xiàn)了Runnable接口 

final Thread t = getThreadFactory().newThread(w

t.start()

看到?jīng)]有,其實調(diào)用的是Worker的run()方法,然后在Worker的run()方法的內(nèi)部又會進行很多處理,最后再去調(diào)用我們傳進去的那個run()方法。5.總結(jié)  其實個人感覺深入到這里就差不多了,基本上就理解了線程池的流程,當(dāng)然有興趣的小伙伴可以繼續(xù)深入看看Worker中的run()方法是怎么執(zhí)行的,其實比較容易,主要是由很多對線程池很多狀態(tài)啊,線程數(shù)量等判斷可能會干擾我們的理解,如果后續(xù)有需要我們再慢慢深入,哈哈哈!  這一篇其實沒說多少內(nèi)容,就是讓大家對線程池到底是個什么鬼有個最粗略的認(rèn)識,其實本質(zhì)就是一個HashSet<Worker>,然后把我們創(chuàng)建的Runnable()實例先給封裝成Worker對象,其中Worker也是實現(xiàn)了Runnable接口,有點類似裝飾者模式,哈哈!再之后就是將WOrker存到那個集合中,并且就調(diào)用start()方法調(diào)用worker的run()方法,然后最后可能就是調(diào)用我們傳進去的那個run()方法了

相關(guān)文章

  • java實現(xiàn)2048小游戲

    java實現(xiàn)2048小游戲

    這篇文章主要為大家詳細(xì)介紹了java實現(xiàn)2048小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • java 實現(xiàn)判斷回文數(shù)字的實例代碼

    java 實現(xiàn)判斷回文數(shù)字的實例代碼

    這篇文章主要介紹了java 實現(xiàn)判斷回文數(shù)字的實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Eclipse將Maven項目打成jar包的方法

    Eclipse將Maven項目打成jar包的方法

    這篇文章主要介紹了Eclipse將Maven項目打成jar包的方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2007-09-09
  • java虛擬機深入學(xué)習(xí)之內(nèi)存管理機制

    java虛擬機深入學(xué)習(xí)之內(nèi)存管理機制

    java虛擬機在程序運行時將內(nèi)存劃分為多個區(qū)域,每個區(qū)域作用,生命周期各不相同,下面這篇文章主要給大家介紹了關(guān)于java虛擬機深入學(xué)習(xí)之內(nèi)存管理機制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-11-11
  • java多線程消息隊列的實現(xiàn)代碼

    java多線程消息隊列的實現(xiàn)代碼

    本篇文章主要介紹了java多線程消息隊列的實現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • SpringBoot使用Thymeleaf自定義標(biāo)簽的實例代碼

    SpringBoot使用Thymeleaf自定義標(biāo)簽的實例代碼

    這篇文章主要介紹了SpringBoot使用Thymeleaf自定義標(biāo)簽的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • sql于navicat中能運行在mybatis中不能運行的解決方案

    sql于navicat中能運行在mybatis中不能運行的解決方案

    這篇文章主要介紹了sql于navicat中能運行在mybatis中不能運行的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • JAVA 深層拷貝 DeepCopy的使用詳解

    JAVA 深層拷貝 DeepCopy的使用詳解

    最近需要用到比較兩個對象屬性的變化,其中一個是oldObj,另外一個是newObj,oldObj是newObj的前一個狀態(tài),所以需要在newObj的某個狀態(tài)時,復(fù)制一個一樣的對象,由于JAVA不支持深層拷貝,因此專門寫了一個方法
    2013-07-07
  • java byte數(shù)組與int,long,short,byte的轉(zhuǎn)換實現(xiàn)方法

    java byte數(shù)組與int,long,short,byte的轉(zhuǎn)換實現(xiàn)方法

    下面小編就為大家?guī)硪黄猨ava byte數(shù)組與int,long,short,byte的轉(zhuǎn)換實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-10-10
  • SPRING IOC注入方式過程解析

    SPRING IOC注入方式過程解析

    這篇文章主要介紹了SPRING IOC注入方式過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01

最新評論