<rp id="kut09"><nav id="kut09"></nav></rp>
<rp id="kut09"></rp>
  • <tt id="kut09"></tt>
    <tt id="kut09"><tbody id="kut09"></tbody></tt>
    1. <u id="kut09"></u>
    2. <tt id="kut09"><noscript id="kut09"></noscript></tt>
    3. Tomcat使用線程池處理遠程并發請求的方法

       更新時間:2020年12月25日 09:01:07   作者:Narule  
      這篇文章主要介紹了Tomcat使用線程池處理遠程并發請求的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

      通過了解學習tomcat如何處理并發請求,了解到線程池,鎖,隊列,unsafe類,下面的主要代碼來自

      java-jre:

      sun.misc.Unsafe
      java.util.concurrent.ThreadPoolExecutor
      java.util.concurrent.ThreadPoolExecutor.Worker
      java.util.concurrent.locks.AbstractQueuedSynchronizer
      java.util.concurrent.locks.AbstractQueuedLongSynchronizer
      java.util.concurrent.LinkedBlockingQueue

      tomcat:

      org.apache.tomcat.util.net.NioEndpoint
      org.apache.tomcat.util.threads.ThreadPoolExecutor
      org.apache.tomcat.util.threads.TaskThreadFactory
      org.apache.tomcat.util.threads.TaskQueue

      ThreadPoolExecutor

      是一個線程池實現類,管理線程,減少線程開銷,可以用來提高任務執行效率,

      構造方法中的參數有

      public ThreadPoolExecutor(
       int corePoolSize,
       int maximumPoolSize,
       long keepAliveTime,
       TimeUnit unit,
       BlockingQueue<Runnable> workQueue,
       ThreadFactory threadFactory,
       RejectedExecutionHandler handler) {
       
      }

      corePoolSize 是核心線程數
      maximumPoolSize 是最大線程數
      keepAliveTime 非核心線程最大空閑時間(超過時間終止)
      unit 時間單位
      workQueue 隊列,當任務過多時,先存放在隊列
      threadFactory 線程工廠,創建線程的工廠
      handler 決絕策略,當任務數過多,隊列不能再存放任務時,該如何處理,由此對象去處理。這是個接口,你可以自定義處理方式

      ThreadPoolExecutor在Tomcat中http請求的應用

      此線程池是tomcat用來在接收到遠程請求后,將每次請求單獨作為一個任務去處理,每次調用execute(Runnable)

      初始化

      org.apache.tomcat.util.net.NioEndpoint

      NioEndpoint初始化的時候,創建了線程池

      public void createExecutor() {
       internalExecutor = true;
       TaskQueue taskqueue = new TaskQueue();
       //TaskQueue無界隊列,可以一直添加,因此handler 等同于無效
       TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
       executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
       taskqueue.setParent( (ThreadPoolExecutor) executor);
       }

      在線程池創建時,調用prestartAllCoreThreads(), 初始化核心工作線程worker,并啟動

      public int prestartAllCoreThreads() {
       int n = 0;
       while (addWorker(null, true))
        ++n;
       return n;
       }

      當addWorker 數量等于corePoolSize時,addWorker(null,ture)會返回false,停止worker工作線程的創建

      提交任務到隊列

      每次客戶端過來請求(http),就會提交一次處理任務,

      worker 從隊列中獲取任務運行,下面是任務放入隊列的邏輯代碼

      ThreadPoolExecutor.execute(Runnable) 提交任務:

      public void execute(Runnable command) {
       if (command == null)
        throw new NullPointerException();
       
       int c = ctl.get();
       	// worker數 是否小于 核心線程數 tomcat中初始化后,一般不滿足第一個條件,不會addWorker
       if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
        return;
        c = ctl.get();
       }
       	// workQueue.offer(command),將任務添加到隊列,
       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);
       }
       else if (!addWorker(command, false))
        reject(command);
       }

      workQueue.offer(command) 完成了任務的提交(在tomcat處理遠程http請求時)。

      workQueue.offer

      TaskQueue 是 BlockingQueue 具體實現類,workQueue.offer(command)實際代碼:

      public boolean offer(E e) {
       if (e == null) throw new NullPointerException();
       final AtomicInteger count = this.count;
       if (count.get() == capacity)
       return false;
       int c = -1;
       Node<E> node = new Node<E>(e);
       final ReentrantLock putLock = this.putLock;
       putLock.lock();
       try {
       if (count.get() < capacity) {
        enqueue(node); //此處將任務添加到隊列
        c = count.getAndIncrement();
        if (c + 1 < capacity)
        notFull.signal();
       }
       } finally {
       putLock.unlock();
       }
       if (c == 0)
       signalNotEmpty();
       return c >= 0;
      }
      
      // 添加任務到隊列
      /**
       * Links node at end of queue.
       *
       * @param node the node
       */
      private void enqueue(Node<E> node) {
       // assert putLock.isHeldByCurrentThread();
       // assert last.next == null;
       last = last.next = node; //鏈表結構 last.next = node; last = node
      }

      之后是worker的工作,worker在run方法中通過去getTask()獲取此處提交的任務,并執行完成任務。

      線程池如何處理新提交的任務

      添加worker之后,提交任務,因為worker數量達到corePoolSize,任務都會將放入隊列,而worker的run方法則是循環獲取隊列中的任務(不為空時),

      worker run方法:

      /** Delegates main run loop to outer runWorker */
       public void run() {
        runWorker(this);
       }

      循環獲取隊列中的任務

      runWorker(worker)方法 循環部分代碼:

      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(); // 上鎖
        try {
         // 運行前處理
         beforeExecute(wt, task);
         // 隊列中的任務開始執行
         task.run();
         // 運行后處理
         afterExecute(task, thrown);
        } finally {
         task = null;
         w.completedTasks++;
         w.unlock(); // 釋放鎖
        }
        }
        completedAbruptly = false;
       } finally {
        processWorkerExit(w, completedAbruptly);
       }
       }

      task.run()執行任務

      鎖運用

      ThreadPoolExecutor 使用鎖主要保證兩件事情,
      1.給隊列添加任務,保證其他線程不能操作隊列
      2.獲取隊列的任務,保證其他線程不能同時操作隊列

      給隊列添加任務上鎖

      public boolean offer(E e) {
       if (e == null) throw new NullPointerException();
       final AtomicInteger count = this.count;
       if (count.get() == capacity)
        return false;
       int c = -1;
       Node<E> node = new Node<E>(e);
       final ReentrantLock putLock = this.putLock;
       putLock.lock(); //上鎖
       try {
        if (count.get() < capacity) {
        enqueue(node);
        c = count.getAndIncrement();
        if (c + 1 < capacity)
         notFull.signal();
        }
       } finally {
        putLock.unlock(); //釋放鎖
       }
       if (c == 0)
        signalNotEmpty();
       return c >= 0;
       }

       

      獲取隊列任務上鎖

      private Runnable getTask() {
       boolean timedOut = false; // Did the last poll() time out?
      		// ...省略
       for (;;) {
        try {
        Runnable r = timed ?
         workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
         workQueue.take(); //獲取隊列中一個任務
        if (r != null)
         return r;
        timedOut = true;
        } catch (InterruptedException retry) {
        timedOut = false;
        }
       }
       }
      public E take() throws InterruptedException {
       E x;
       int c = -1;
       final AtomicInteger count = this.count;
       final ReentrantLock takeLock = this.takeLock;
       takeLock.lockInterruptibly(); // 上鎖
       try {
        while (count.get() == 0) {
        notEmpty.await(); //如果隊列中沒有任務,等待
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
        notEmpty.signal();
       } finally {
        takeLock.unlock(); // 釋放鎖
       }
       if (c == capacity)
        signalNotFull();
       return x;
       }

      volatile

      在并發場景這個關鍵字修飾成員變量很常見,

      主要目的公共變量在被某一個線程修改時,對其他線程可見(實時)

      sun.misc.Unsafe 高并發相關類

      線程池使用中,有平凡用到Unsafe類,這個類在高并發中,能做一些原子CAS操作,鎖線程,釋放線程等。

      sun.misc.Unsafe 類是底層類,openjdk源碼中有

      原子操作數據

      java.util.concurrent.locks.AbstractQueuedSynchronizer 類中就有保證原子操作的代碼

      protected final boolean compareAndSetState(int expect, int update) {
       // See below for intrinsics setup to support this
       return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
       }

      對應Unsafe類的代碼:

      //對應的java底層,實際是native方法,對應C++代碼
      /**
      * Atomically update Java variable to <tt>x</tt> if it is currently
      * holding <tt>expected</tt>.
      * @return <tt>true</tt> if successful
      */
      public final native boolean compareAndSwapInt(Object o, long offset,
            int expected,
            int x);

      方法的作用簡單來說就是 更新一個值,保證原子性操作
      當你要操作一個對象o的一個成員變量offset時,修改o.offset,
      高并發下為保證準確性,你在操作o.offset的時候,讀應該是正確的值,并且中間不能被別的線程修改來保證高并發的環境數據操作有效。

      即 expected 期望值與內存中的值比較是一樣的expected == 內存中的值 ,則更新值為 x,返回true代表修改成功

      否則,期望值與內存值不同,說明值被其他線程修改過,不能更新值為x,并返回false,告訴操作者此次原子性修改失敗。

      阻塞和喚醒線程

      public native void park(boolean isAbsolute, long time); //阻塞當前線程

      線程池的worker角色循環獲取隊列任務,如果隊列中沒有任務,worker.run 還是在等待的,不會退出線程,代碼中用了notEmpty.await() 中斷此worker線程,放入一個等待線程隊列(區別去任務隊列);當有新任務需要時,再notEmpty.signal()喚醒此線程

      底層分別是
      unsafe.park() 阻塞當前線程
      public native void park(boolean isAbsolute, long time);

      unsafe.unpark() 喚醒線程
      public native void unpark(Object thread);

      這個操作是對應的,阻塞時,先將thread放入隊列,喚醒時,從隊列拿出被阻塞的線程,unsafe.unpark(thread)喚醒指定線程。

      java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject 類中

      通過鏈表存放線程信息

      // 添加一個阻塞線程
      private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
        }
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
        if (t == null)
        firstWaiter = node;
        else
        t.nextWaiter = node;
        lastWaiter = node; //將新阻塞的線程放到鏈表尾部
        return node;
       }
      
      // 拿出一個被阻塞的線程
       public final void signal() {
        if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
        Node first = firstWaiter; //鏈表中第一個阻塞的線程
        if (first != null)
        doSignal(first);
       }
      
      // 拿到后,喚醒此線程
      final boolean transferForSignal(Node node) {
        LockSupport.unpark(node.thread);
       return true;
       }
      public static void unpark(Thread thread) {
       if (thread != null)
        UNSAFE.unpark(thread);
       }

      到此這篇關于Tomcat使用線程池處理遠程并發請求的方法的文章就介紹到這了,更多相關Tomcat線程池處理遠程并發請求內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

      相關文章

      • tomcat異常解決(Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986)

        tomcat異常解決(Invalid character found in the request target

        這篇文章主要介紹了tomcat 異常的解決方案,幫助大家排查錯誤,保持服務器的穩定,感興趣的朋友可以了解下
        2020-10-10
      • 詳解Tomcat出現404的解決方法

        詳解Tomcat出現404的解決方法

        這篇文章主要介紹了詳解Tomcat出現404的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
        2020-08-08
      • Tomcat ssl報錯Connector attribute SSLCertificateFile must be defined when using SSL with APR解決方法

        Tomcat ssl報錯Connector attribute SSLCertificateFile must be

        這篇文章主要介紹了Tomcat ssl報錯Connector attribute SSLCertificateFile must be defined when using SSL with APR解決方法,需要的朋友可以參考下
        2014-12-12
      • Tomcat與JDK版本對應關系以及Tomcat各版本特性

        Tomcat與JDK版本對應關系以及Tomcat各版本特性

        這篇文章主要介紹了Tomcat與JDK版本對應關系以及Tomcat各版本特性,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
        2019-11-11
      • Tomcat 部署項目的三種方法詳解

        Tomcat 部署項目的三種方法詳解

        本篇文章主要介紹了Tomcat 部署項目的三種方法詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
        2017-06-06
      • Tomcat首次部署web項目流程圖解

        Tomcat首次部署web項目流程圖解

        這篇文章主要介紹了Tomcat首次部署web項目流程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
        2020-12-12
      • Tomcat下載安裝并部署到IDEA的教程(附帶idea兩種熱部署設置方法)

        Tomcat下載安裝并部署到IDEA的教程(附帶idea兩種熱部署設置方法

        這篇文章主要介紹了Tomcat下載安裝并部署到IDEA的教程(附帶idea兩種熱部署設置方法),本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
        2019-12-12
      • GZIP壓縮Tomcat并提升web性能過程圖解

        GZIP壓縮Tomcat并提升web性能過程圖解

        這篇文章主要介紹了GZIP壓縮Tomcat并提升web性能過程圖解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
        2020-04-04
      • Tomcat啟動springboot項目war包報錯:啟動子級時出錯的問題

        Tomcat啟動springboot項目war包報錯:啟動子級時出錯的問題

        這篇文章主要介紹了Tomcat啟動springboot項目war包報錯:啟動子級時出錯的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
        2020-08-08
      • 搭建Tomcat 8源碼開發環境的步驟詳解

        搭建Tomcat 8源碼開發環境的步驟詳解

        相信大家都知道開源軟件tomcat目前幾乎已經是Java web開發的必備軟件了,目前有很多關于tomcat的書籍,已經通過配置對tomcat進行一些跟應用業務功能的調優,但感覺如果僅僅只是了解一些配置,可能稍微少了點什么,下面通過本文深入到源代碼中進行學些和了解。
        2016-10-10

      最新評論

      218彩票 www.amnestyforanimals.org:交城县| www.revyveskin.org:渝北区| www.radiocachora.com:济宁市| www.tjmtw.com:瑞丽市| www.chris-sabin.com:乾安县| www.gztaiji.cn:灯塔市| www.thuebannhadat.net:东辽县| www.tswtchkviii.net:苍南县| www.jeanpellissier.com:外汇| www.iqhausa.com:江口县| www.anthemgamegroup.com:灵台县| www.buyijiang.com:板桥市| www.09dn.com:上高县| www.bildungerziehung.org:白城市| www.soccer-cleats-usa.com:通州市| www.tjshuxin.com:五华县| www.businessptr.com:同心县| www.cp6331.com:邢台市| www.zen-moa-massage.com:罗源县| www.mp337.com:洛隆县| www.aroyalhangover.com:商丘市| www.headsion.cn:湖北省| www.hbtzn.com:兴业县| www.edenspringshotel.com:新竹市| www.kq266.com:平潭县| www.shamrockestatesaz.com:咸丰县| www.shipwatch.org:定远县| www.cp8559.com:疏附县| www.asksworld.com:六盘水市| www.african-solar.com:湘乡市| www.uggaugga.com:望都县| www.tjssanreqi.com:绥阳县| www.vertaxtechnology.com:赤壁市| www.reitzhausproductions.com:嵊州市| www.z8689.com:红原县| www.tykxzz.com:河北区| www.cp3115.com:衡山县| www.netcnz.com:赤水市| www.lunglinks.com:务川| www.km-alliance.com:巢湖市| www.byopi.com:阿鲁科尔沁旗| www.tuoheng-china.com:民勤县| www.cxm5.com:岗巴县| www.stmgqhw.com:凯里市| www.vipsus.com:六盘水市| www.omymedia.com:安化县| www.mybzw.com:沭阳县| www.bigbanganimation.com:平邑县| www.makpad.com:扬州市| www.cardriverentacar.com:锡林郭勒盟| www.fnp-co.com:时尚| www.aeul-subs.com:从化市| www.mikenatalizio.com:自贡市| www.shilongwangcn.com:边坝县| www.no-flash.com:故城县| www.mfwwn.com:富民县| www.82aaaa.com:九江市| www.friendlyny.com:通辽市| www.lunwentao.com:长白| www.oklahomatrivia.com:綦江县| www.syyunshengs.com:淳化县| www.fzjiulong.com:彩票| www.tl0553.com:和平区| www.gm445.com:秀山| www.fgzcs.com:房产| www.goodgirltoys.com:宁晋县| www.synergistichealthgb.com:漠河县| www.cdxufeng.com:武义县| www.683521.com:全椒县| www.plastic-films.com:武陟县| www.cillianmurphy.net:竹山县| www.kocblog.com:农安县| www.suntopcar.com:米易县| www.uniahes.com:文成县| www.webefendi.com:孟村| www.hg75456.com:芜湖县| www.shahidhashmi.net:佛教| www.cafeavec.com:新巴尔虎右旗| www.oubok.com:紫云| www.phuengoat.com:高尔夫| www.hongxinyu888.com:万年县| www.antski.com:连云港市| www.j0662.com:宁波市| www.hougangopenmri.com:和平县|