<font id="nc9yk"></font>
  • <tt id="nc9yk"></tt>
          <rp id="nc9yk"><optgroup id="nc9yk"></optgroup></rp>
          <tt id="nc9yk"><form id="nc9yk"></form></tt>

            <cite id="nc9yk"></cite>

            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

            最新評論

            hao500彩票 www.jipiao126.com:鄂托克旗| www.yzzzm.com:绥芬河市| www.ditr-inc.com:文成县| www.q420gb.com:高密市| www.sproutstudio.net:若羌县| www.lwshengfeng.com:江川县| www.3654388.com:汉川市| www.cccmlogistics.com:衡阳县| www.ate77.com:乌兰浩特市| www.taipeisailing.org:伊金霍洛旗| www.gutajiao.com:礼泉县| www.madisonkungfu.com:东阳市| www.davisdeyoe.com:镇江市| www.itbruce.com:永兴县| www.antski.com:沙湾县| www.z3858.com:股票| www.4-card-poker-online.com:四会市| www.tp633.com:新田县| www.d3mm.com:莲花县| www.videodownloadming.com:贵港市| www.value-jp.com:阿坝| www.surridgesmusiccentre.com:长治县| www.nmgshanhua.com:江门市| www.carahedgepeth.com:临沧市| www.sllgj.com:怀远县| www.ottocargo.com:四川省| www.jeanpellissier.com:宜丰县| www.flex-laser.net:亳州市| www.excelsisairways.com:奎屯市| www.szbxmchess.com:济宁市| www.fsygr.cn:芷江| www.mezew.com:浦江县| www.curtisdemarce.com:雷波县| www.hendry-l.com:连江县| www.n048.com:资源县| www.gzbjbgs.com:洛扎县| www.mr-impact-windows.com:扬州市| www.krowstore.com:沙洋县| www.dawntoner.com:梁河县| www.ebikemoto.com:西畴县| www.rdzfw.com:汕尾市| www.rabarg.com:会理县| www.burkholderpaving.com:房山区| www.efemlak.com:昆明市| www.african-solar.com:忻城县| www.nightsailer.com:德令哈市| www.wwwableton.com:大厂| www.dalicun.com:炉霍县| www.mf-moto.com:松滋市| www.gcyy-120.com:齐齐哈尔市| www.jfc-grp.com:定襄县| www.cbsrhelp.com:衡阳市| www.alshamdc.com:九台市| www.earmaps.com:高唐县| www.postcanal.com:军事| www.jm88dz.com:鸡东县| www.lalshahbaz.com:威远县| www.tongmould.com:乌兰察布市| www.anlson.com:会东县| www.92top10.com:南充市| www.564rr.com:四川省| www.smashingoffernow.com:曲松县| www.ninaseattle.com:合阳县| www.9959gp.com:阿克陶县| www.carahedgepeth.com:闻喜县| www.beautysalonsolutions.com:怀仁县| www.wrennak.com:留坝县| www.gaobaoit.com:惠州市| www.8888cngroup.com:宁都县| www.aumetrodeslilas.com:海安县| www.soledoubtshow.com:静海县| www.qhzxz.com:白玉县| www.hg39799.com:秦皇岛市| www.bildungerziehung.org:华坪县| www.welcolan.com:连南| www.hg97345.com:梨树县| www.eqmadmin.com:炎陵县| www.dianehamburg.com:天祝| www.cp7990.com:格尔木市| www.bfgw.org:徐汇区| www.jas-cn.com:绥芬河市| www.djosephoto.com:滨州市| www.edcvanuatu.com:中阳县| www.gumur.com:贵德县|