整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          超實用!學習dubbo如何優(yōu)雅關閉線程池

          超實用!學習dubbo如何優(yōu)雅關閉線程池

          薦閱讀:

          • 騰訊T4大佬總結(jié):微服務、SpringBoot、SpringCloud、Dubbo文檔

          基本方法學習

          線程池是我們經(jīng)常使用的工具,也是面試必問的知識點,那么如何優(yōu)雅的關閉線程池那?

          線程池相信大家都使用過,但是使用完成之后如何關閉線程池大家真的未必真的會使用。有人可能會說調(diào)用shutdown或者shutdownNow就可以了,真的有那么簡單嗎?如果你關閉線程池的姿勢不正確,最嚴重的情況會導致線程一直存在系統(tǒng)中。

          • shutDown:通知線程池啟動有序關閉,執(zhí)行線程池之前已經(jīng)提交的任務,但是不再接受新的任務。調(diào)用shutDown后再提交任務將會拋出RejectedExecutionException異常。
          • shutDownNow:嘗試立即停止所有已經(jīng)提交的任務,并會返回正在等待執(zhí)行(未執(zhí)行)的任務列表。shutDownNow通過向線程池中的線程發(fā)送一個中斷請求而中止線程,如果線程池中運行了會拋出InterruptedException的程序,將會拋出一個InterruptedException。如過這個線程不能響應中斷那么可能永遠無法被終止。
          • isTerminated:所有的任務都被關閉返回true,否則返回false。只有調(diào)用了shutDown或者shutDownNow,isTerminated才可能為true。
          • awaitTermination(long timeout, TimeUnit unit) throws InterruptedException:阻塞當前線程直到 所有任務執(zhí)行完畢 或者超時 或者當前線程被中斷 如果所有任務都關閉,則返回true,否則返回false。

          優(yōu)雅關閉線程池的正確姿勢

          • step1:執(zhí)行shutdown方法,等待所有任務執(zhí)行完畢并拒絕新任務的提交。
          • step2:執(zhí)行awaitTermination(long timeout,TimeUnit unit),指定超時時間,判斷是是否已經(jīng)關閉所有任務,防止線程永遠無法關閉。
          • step3:如果step2返回fasle,或者被中斷。調(diào)用shutDownNow方法立即關閉線程池所有任務。

          dubbo關閉線程池工具類學習

          public class ExecutorUtil {
              private static final Logger logger=LoggerFactory.getLogger(ExecutorUtil.class);
              private static final ThreadPoolExecutor shutdownExecutor=new ThreadPoolExecutor(0, 1,
                      0L, TimeUnit.MILLISECONDS,
                      new LinkedBlockingQueue<Runnable>(100),
                      new NamedThreadFactory("Close-ExecutorService-Timer", true));
          
              public static boolean isTerminated(Executor executor) {
                  if (executor instanceof ExecutorService) {
                      if (((ExecutorService) executor).isTerminated()) {
                          return true;
                      }
                  }
                  return false;
              }
          
              /**
               * Use the shutdown pattern from:
               *  https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
               * @param executor the Executor to shutdown
               * @param timeout the timeout in milliseconds before termination
               */
              public static void gracefulShutdown(Executor executor, int timeout) {
                  if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
                      return;
                  }
                  final ExecutorService es=(ExecutorService) executor;
                  try {
                      // Disable new tasks from being submitted
                      es.shutdown();
                  } catch (SecurityException ex2) {
                      return;
                  } catch (NullPointerException ex2) {
                      return;
                  }
                  try {
                      // Wait a while for existing tasks to terminate
                      if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
                          es.shutdownNow();
                      }
                  } catch (InterruptedException ex) {
                      es.shutdownNow();
                      Thread.currentThread().interrupt();
                  }
                  if (!isTerminated(es)) {
                      newThreadToCloseExecutor(es);
                  }
              }
          
              public static void shutdownNow(Executor executor, final int timeout) {
                  if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
                      return;
                  }
                  final ExecutorService es=(ExecutorService) executor;
                  try {
                      es.shutdownNow();
                  } catch (SecurityException ex2) {
                      return;
                  } catch (NullPointerException ex2) {
                      return;
                  }
                  try {
                      es.awaitTermination(timeout, TimeUnit.MILLISECONDS);
                  } catch (InterruptedException ex) {
                      Thread.currentThread().interrupt();
                  }
                  if (!isTerminated(es)) {
                      newThreadToCloseExecutor(es);
                  }
              }
          
              private static void newThreadToCloseExecutor(final ExecutorService es) {
                  if (!isTerminated(es)) {
                      shutdownExecutor.execute(new Runnable() {
                          @Override
                          public void run() {
                              try {
                                  for (int i=0; i < 1000; i++) {
                                      es.shutdownNow();
                                      if (es.awaitTermination(10, TimeUnit.MILLISECONDS)) {
                                          break;
                                      }
                                  }
                              } catch (InterruptedException ex) {
                                  Thread.currentThread().interrupt();
                              } catch (Throwable e) {
                                  logger.warn(e.getMessage(), e);
                              }
                          }
                      });
                  }
              }
          }


          作者:克里斯朵夫李維
          鏈接:https://juejin.im/post/5e9ec57df265da47c9171456

          avaScript 的 Event Loop(事件循環(huán))是 JavaScript 運行時環(huán)境(如瀏覽器和 Node.js)的核心機制之一,它使得 JavaScript 能夠處理異步操作而不會阻塞程序的執(zhí)行。了解 Event Loop 對于理解 JavaScript 的非阻塞行為和編寫高效的異步代碼至關重要。

          1. JavaScript 是單線程的

          首先,重要的是要理解 JavaScript 是一種單線程的語言。這意味著 JavaScript 在同一時間內(nèi)只能執(zhí)行一個任務。然而,JavaScript 需要能夠處理各種異步操作(如 AJAX 請求、文件讀取、用戶交互等),這些操作可能會花費很長時間完成。為了解決這個問題,JavaScript 采用了 Event Loop 和 Callback Queues(回調(diào)隊列)。

          2. 調(diào)用棧(Call Stack)

          調(diào)用棧是 JavaScript 代碼執(zhí)行時的數(shù)據(jù)結(jié)構(gòu),用于存儲函數(shù)調(diào)用和返回地址。每當一個函數(shù)被調(diào)用時,它就會被推入調(diào)用棧,并在函數(shù)執(zhí)行完畢后從棧中彈出。如果調(diào)用棧滿了(即達到了最大調(diào)用深度),則會發(fā)生棧溢出錯誤。

          3. 堆(Heap)

          堆是用于存儲對象、數(shù)組等引用類型的內(nèi)存區(qū)域。與調(diào)用棧不同,堆是動態(tài)分配的,并且其大小不是固定的。

          4. Web APIs

          Web APIs 是瀏覽器提供的一組與瀏覽器功能交互的接口,如 DOM 操作、網(wǎng)絡請求等。這些 API 通常是異步的,并且它們有自己的線程或進程來處理請求。

          5. 任務隊列(Task Queue)和微任務隊列(Microtask Queue)

          當異步操作完成時(如 AJAX 請求、setTimeout、Promise 解決等),相應的回調(diào)函數(shù)會被放入任務隊列(或稱為宏任務隊列)或微任務隊列中。任務隊列中的任務在當前的執(zhí)行棧清空后才會被執(zhí)行,而微任務隊列中的任務會在當前執(zhí)行棧清空后、但下一個宏任務執(zhí)行前立即執(zhí)行。

          6. Event Loop 的工作原理

          Event Loop 的工作流程可以概括為以下幾個步驟:

          1. 檢查調(diào)用棧:如果調(diào)用棧為空,則繼續(xù);如果調(diào)用棧不為空,則等待直到調(diào)用棧為空。
          2. 執(zhí)行微任務隊列:一旦調(diào)用棧為空,Event Loop 就會查看微任務隊列是否有任務。如果有,它會依次執(zhí)行微任務隊列中的所有任務,然后再回到第一步。
          3. 執(zhí)行宏任務隊列:在所有微任務都執(zhí)行完畢后,Event Loop 會從宏任務隊列中取出一個任務放入調(diào)用棧執(zhí)行。這個過程會不斷重復。

          7. 常見的宏任務和微任務

          • 宏任務(Macrotasks):包括 script(整體代碼)、setTimeout、setInterval、setImmediate(Node.js 環(huán)境)、I/O、UI rendering 等。
          • 微任務(Microtasks):包括 Promise.then、Promise.catch、Promise.finally、MutationObserver、process.nextTick(Node.js 環(huán)境)等。

          實例 1:setTimeout 和 Promise

          console.log('1');  
            
          setTimeout(()=> {  
            console.log('setTimeout 宏任務隊列');  
          }, 0);  
            
          new Promise((resolve)=> {  
            console.log('Promise 立即執(zhí)行');  
            resolve();  
          }).then(()=> {  
            console.log('then 微任務隊列');  
          });  
            
          console.log('2');
          
          //輸出順序
          1  
          Promise 立即執(zhí)行 
          2  
          then  微任務隊列
          setTimeout 宏任務隊列

          解釋

          1. 首先,執(zhí)行同步代碼,輸出 1。
          2. 然后,setTimeout 被調(diào)用,但因為它是一個宏任務,所以它的回調(diào)函數(shù)被放入宏任務隊列中等待。
          3. 接下來,new Promise 的構(gòu)造函數(shù)被調(diào)用,立即執(zhí)行并輸出 Promise。resolve() 被調(diào)用,但 .then() 中的回調(diào)函數(shù)是異步的,并且是一個微任務,所以它會被放入微任務隊列中。
          4. 同步代碼繼續(xù)執(zhí)行,輸出 2。
          5. 當所有同步代碼執(zhí)行完畢后,Event Loop 開始處理微任務隊列。它找到 .then() 的回調(diào)函數(shù)并執(zhí)行,輸出 then。
          6. 最后,當微任務隊列為空時,Event Loop 轉(zhuǎn)到宏任務隊列,執(zhí)行 setTimeout 的回調(diào)函數(shù),輸出 setTimeout。

          實例 2:多個 Promise 和 setTimeout

          console.log('1');  
            
          setTimeout(()=> {  
            console.log('setTimeout  宏任務隊列1');  
            new Promise((resolve)=> {  
              console.log('Promise in setTimeout');  
              resolve();  
            }).then(()=> {  
              console.log('then in setTimeout');  
            });  
            setTimeout(()=> {  
              console.log('setTimeout 宏任務隊列2');  
            }, 0);  
          }, 0);  
            
          new Promise((resolve)=> {  
            console.log('Promise 立即執(zhí)行1');  
            resolve();  
          }).then(()=> {  
            console.log('then 微任務隊列1');  
            new Promise((resolve)=> {  
              console.log('Promise 立即執(zhí)行2');  
              resolve();  
            }).then(()=> {  
              console.log('then 微任務隊列2');  
            });  
          });  
            
          console.log('2');
          
          //輸出順序
          1 
          Promise 立即執(zhí)行1  
          2  
          then 微任務隊列1 
          Promise 立即執(zhí)行2  
          then 微任務隊列2
          setTimeout  宏任務隊列1  
          Promise in setTimeout  
          then in setTimeout  
          setTimeout  宏任務隊列2

          解釋

          1. 同步代碼首先執(zhí)行,輸出 1、Promise 1 和 2。
          2. .then() 中的回調(diào)函數(shù)作為微任務被加入微任務隊列。
          3. 第一個 setTimeout 被調(diào)用,它的回調(diào)函數(shù)被加入宏任務隊列。
          4. 當所有同步代碼執(zhí)行完畢后,開始執(zhí)行微任務隊列中的任務。首先輸出then 微任務隊列1,然后執(zhí)行 Promise 立即執(zhí)行2then 微任務隊列2
          5. 微任務隊列為空后,執(zhí)行宏任務隊列中的第一個任務(即第一個 setTimeout 的回調(diào)函數(shù)),輸出相關日志。
          6. 第二個 setTimeout 的回調(diào)函數(shù)也被加入宏任務隊列,并在當前宏任務執(zhí)行完畢后執(zhí)行。

          實例 3:async/await 與 Promise

          const async1=async ()=> {
             console.log('async1 1');  
             await async2();  
             console.log('async1 2');  
          }
             
          const async2=async ()=> {
             console.log('async2');  
          }
            
          console.log('1');  
          setTimeout(()=> {  
            console.log('setTimeout 宏任務隊列');  
          }, 0);  
            
          async1();  
            
          new Promise((resolve)=> {  
            console.log('promise 立即執(zhí)行');  
            resolve();  
          }).then(()=> {  
            console.log('then 微任務隊列');  
          });  
            
          console.log('2');
          
          //輸出順序
          1
          async1 1
          async2  
          promise 立即執(zhí)行
          2
          async1 2
          then 微任務隊列
          setTimeout 宏任務隊列

          解釋

          1. 同步代碼首先執(zhí)行,輸出1。
          2. async1() 被調(diào)用,輸出async1 1。
          3. await async2() 暫停 async1() 的執(zhí)行,async2() 被調(diào)用并輸出 async2。因為 async2() 沒有返回 Promise 或沒有等待的異步操作,所以 await 后面的代碼在 async2() 執(zhí)行完畢后繼續(xù)執(zhí)行。
          4. 同步代碼首先執(zhí)行,輸出promise 立即執(zhí)行和2。
          5. 之后async2執(zhí)行完畢后,同步代碼輸出async1 2,
          6. 當所有同步代碼執(zhí)行完畢后,開始執(zhí)行微任務隊列中的任務then 微任務隊列
          7. 最后執(zhí)行宏任務隊列,輸出setTimeout 宏任務隊列

          結(jié)論

          Event Loop 是 JavaScript 異步編程的基石,它使得 JavaScript 能夠在不阻塞主線程的情況下處理各種異步操作。通過理解 Event Loop 的工作原理,我們可以更加高效地編寫異步代碼,避免潛在的錯誤和性能問題。

          一步:解析 HTML

          1. 解析過程中遇到 CSS,解析 CSS;遇到 JS ,執(zhí)行 JS;為了提高效率,瀏覽器在開始解析之前,會啟動一個預解析線程,率先下載 HTML 中外部的 CSS 文件和外部的 JS 文件;
          2. 如果主線程解析到 link位置時,此時外部的 CSS 文件還沒有下載解析完成,主線程不會等待,繼續(xù)執(zhí)行后續(xù)代碼,因為 CSS 下載和解析工作是在預解析線程中進行的,這也是CSS不會阻塞 HTML 解析的根本原因
          3. 如果主線程解析到 script 位置時;會停止解析 HTML,而去等待 JS文件下載完畢,并將全部代碼執(zhí)行完成后,才會繼續(xù)解析 HTML;這是因為 JS 代碼在執(zhí)行的過程中有可能會修改當前的DOM樹;所以DOM 樹的生成必須停止,這也就是 JS 會阻塞HTML 解析的根本原因;

          第二步:樣式計算

          1. 主線程會遍歷得到的 DOM 樹;依次為每個 DOM 樹的節(jié)點計算出最終樣式;
          2. 在這一過程中,很多相對值會變成絕對值;例如:16進制顏色值會變成rgb顏色值,rem、em、vw會變成px;

          1. 第三步:布局
          2. 布局階段主線程會依次遍歷 DOM 樹的每一個節(jié)點,計算每一個節(jié)點的幾何信息;例如節(jié)點的寬、高、相對包含塊的位置;
          3. 大部分的時候 DOM(元素) 樹和 Layout(布局)樹并非一一對應;
          4. 例如display:none的節(jié)點是沒有幾何信息的;因此不會產(chǎn)生Layout樹、使用了偽元素的選擇器、雖然DOM樹中不存在這些偽元素節(jié)點,但是它們擁有幾何信息,所以會生成Layout樹、還有匿名行盒、匿名塊盒都會導致DOM樹和Layout樹無法一一對應;
          5. 內(nèi)容必須在行盒中,盒子的類型有CSS來決定,不由HTML來決定,HTML只提供語義化;

          第四步:分層

          1. 主線程會通過一套復雜的策略來對整個Layout樹進行分層;
          2. 分層的好處在于將來某一個層改變后,僅會對該層進行后續(xù)處理,不會影響到其它層;從而達到提升效率
          3. 滾動條、堆疊上下文、transform、opacity等樣式都會或多或少的影響分層結(jié)果,也可以通過will-嫦娥屬性來更大程度上影響分層結(jié)果。

          第五步:繪制

          1. 主線程會為每個層單獨產(chǎn)生繪制指令集,用于描述這一層的內(nèi)容如何畫出來;
          2. 繪制完成后,主線程將每個圖層的繪制信息提交給 合成線程,剩余的工作將由合成線程完成。

          第六步:分塊

          1. 合成線程首先對每個圖層進行分塊,將其劃分為更多的小區(qū)域;
          2. 它會從線程池中拿取更多的線程來完成分塊工作;

          第七步:光柵化

          1. 分塊完成后,合成線程將塊信息交給GPU進程,以極高的速度完成光柵化;
          2. GPU 進程會開啟多個線程來完成光柵化,并且優(yōu)先處理靠近視口區(qū)域的塊
          3. 光柵化的結(jié)果就是一塊一塊的位置;

          第八步:

          1. 合成線程拿到層,每個塊的位圖后,生成一個個【指引(quad)】信息;
          2. 指引會標識每個位圖感應該畫到屏幕的哪個位置,以及會考慮旋轉(zhuǎn),縮放等變形;
          3. 變形發(fā)生在合成線程,與渲染主線程無關,這就是transform效率高的本質(zhì)原因;
          4. 合成線程會把quad提交給GPU進程,由GPU進程產(chǎn)生系統(tǒng)調(diào)用,提交給GPU硬件,完成最終的屏幕成像。

          主站蜘蛛池模板: 国产精品美女一区二区三区| 色综合视频一区二区三区44| 天堂一区人妻无码| 麻豆视频一区二区三区| 精品无码一区二区三区在线 | 欧洲精品码一区二区三区| 末成年女AV片一区二区| 久久久国产精品亚洲一区| 国产无套精品一区二区| 狠狠综合久久AV一区二区三区 | 嫩B人妻精品一区二区三区| 国产精品毛片一区二区| 在线精品动漫一区二区无广告| 精品国产亚洲一区二区三区| 中文字幕国产一区| 少妇一夜三次一区二区| 中文字幕在线不卡一区二区| 精品永久久福利一区二区| 中文字幕在线观看一区| 免费无码AV一区二区| 最新中文字幕一区二区乱码| 日韩亚洲AV无码一区二区不卡| 中文无码AV一区二区三区| 伊人精品视频一区二区三区| 久久国产免费一区二区三区| 成人毛片无码一区二区| 成人乱码一区二区三区av| 久久er99热精品一区二区 | 亚洲丰满熟女一区二区v| 日韩视频一区二区在线观看| 精品视频一区二区三区在线观看 | 亚洲av乱码中文一区二区三区| 夜夜爽一区二区三区精品| 久久se精品一区二区国产| 日本一区二区三区在线网 | 亚洲一区无码精品色| 国产AV午夜精品一区二区三| 精品少妇ay一区二区三区 | 国模吧一区二区三区精品视频| 成人区人妻精品一区二区不卡网站| 国产精品综合AV一区二区国产馆|