喜歡的話麻煩點下Star哈
文章首發於我的個人weixin :
c3207130476
Java中的線程
Java之父對線程的定義是:
線程是一個獨立執行的調用序列,同一個進程的線程在同一時刻共享一些系統資源(比如文件句柄等)也能訪問同一個進程所創建的對象資源(內存資源)。java.lang.Thread對象負責統計和控制這種行為。
每個程序都至少擁有一個線程-即作為Java虛擬機(JVM)啟動參數運行在主類main方法的線程。在Java虛擬機初始化過程中也可能啟動其他的后台線程。這種線程的數目和種類因JVM的實現而異。然而所有用戶級線程都是顯式被構造並在主線程或者是其他用戶線程中被啟動。
本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。在這之前,首先讓我們來了解下在操作系統中進程和線程的區別:
進程:每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位) 線程:同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位) 線程和進程一樣分為五個階段:創建、就緒、運行、阻塞、終止。 多進程是指操作系統能同時運行多個任務(程序)。 多線程是指在同一程序中有多個順序流在執行。 在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable接口.(其實准確來講,應該有三種,還有一種是實現Callable接口,並與Future、線程池結合使用
Java線程狀態機
Java 給多線程編程提供了內置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。
多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。
這里定義和線程相關的另一個術語 - 進程:一個進程包括由操作系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結束運行后才能結束。
多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU 的目的。
一個線程的生命周期
線程是一個動態執行的過程,它也有一個從產生到死亡的過程。
下圖顯示了一個線程完整的生命周期。
-
新建狀態:
使用 new 關鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。
-
就緒狀態:
當線程對象調用了start()方法之后,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM里線程調度器的調度。
-
運行狀態:
如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。
-
阻塞狀態:
如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。可以分為三種:
-
等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
-
同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。
-
其他阻塞:通過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉入就緒狀態。
-
-
死亡狀態:
一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。
Java多線程實戰
多線程的實現
public class 多線程實例 {
//繼承thread @Test public void test1() { class A extends Thread { @Override public void run() { System.out.println("A run"); } } A a = new A(); a.start(); } //實現Runnable @Test public void test2() { class B implements Runnable { @Override public void run() { System.out.println("B run"); } } B b = new B(); //Runable實現類需要由Thread類包裝后才能執行 new Thread(b).start(); } //有返回值的線程 @Test public void test3() { Callable callable = new Callable() { int sum = 0; @Override public Object call() throws Exception { for (int i = 0;i < 5;i ++) { sum += i; } return sum; } }; //這里要用FutureTask,否則不能加入Thread構造方法 FutureTask futureTask = new FutureTask(callable); new Thread(futureTask).start(); try { System.out.println(futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } //線程池實現 @Test public void test4() { ExecutorService executorService = Executors.newFixedThreadPool(5); //execute直接執行線程 executorService.execute(new Thread()); executorService.execute(new Runnable() { @Override public void run() { System.out.println("runnable"); } }); //submit提交有返回結果的任務,運行完后返回結果。 Future future = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "a"; } }); try { System.out.println(future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } ArrayList<String> list = new ArrayList<>(); //有返回值的線程組將返回值存進集合 for (int i = 0;i < 5;i ++ ) { int finalI = i; Future future1 = executorService.submit(new Callable<String>() { @Override public String call() throws Exception { return "res" + finalI; } }); try { list.add((String) future1.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } for (String s : list) { System.out.println(s); } }
}
線程狀態轉換
public class 線程的狀態轉換 { //一開始線程是init狀態,結束時是terminated狀態 class t implements Runnable { private String name; public t(String name) { this.name = name; } @Override public void run() { System.out.println(name + "run"); } } //測試join,父線程在子線程運行時進入waiting狀態 @Test public void test1() throws InterruptedException { Thread dad = new Thread(new Runnable() { Thread son = new Thread(new t("son")); @Override public void run() { System.out.println("dad init"); son.start(); try { //保證子線程運行完再運行父線程 son.join(); System.out.println("dad run"); } catch (InterruptedException e) { e.printStackTrace(); } } }); //調用start,線程進入runnable狀態,等待系統調度 dad.start(); //在父線程中對子線程實例使用join,保證子線程在父線程之前執行完 } //測試sleep @Test public void test2(){ Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1 run"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); //主線程休眠。進入time waiting狀態 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } t1.start(); } //線程2進入blocked狀態。 public static void main(String[] args) { test4(); Thread.yield();//進入runnable狀態 } //測試blocked狀態 public static void test4() { class A { //線程1獲得實例鎖以后線程2無法獲得實例鎖,所以進入blocked狀態 synchronized void run() { while (true) { System.out.println("run"); } } } A a = new A(); new Thread(new Runnable() { @Override public void run() { System.out.println("t1 get lock"); a.run(); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println("t2 get lock"); a.run(); } }).start(); } //volatile保證線程可見性 volatile static int flag = 1; //object作為鎖對象,用於線程使用wait和notify方法 volatile static Object o = new Object(); //測試wait和notify //wait后進入waiting狀態,被notify進入blocked(阻塞等待鎖釋放)或者runnable狀態(獲取到鎖) public void test5() { new Thread(new Runnable() { @Override public void run() { //wait和notify只能在同步代碼塊內使用 synchronized (o) { while (true) { if (flag == 0) { try { Thread.sleep(2000); System.out.println("thread1 wait"); //釋放鎖,線程掛起進入object的等待隊列,后續代碼運行 o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread1 run"); System.out.println("notify t2"); flag = 0; //通知等待隊列的一個線程獲取鎖 o.notify(); } } } }).start(); //解釋同上 new Thread(new Runnable() { @Override public void run() { while (true) { synchronized (o) { if (flag == 1) { try { Thread.sleep(2000); System.out.println("thread2 wait"); o.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread2 run"); System.out.println("notify t1"); flag = 1; o.notify(); } } } }).start(); } //輸出結果是 // thread1 run // notify t2 // thread1 wait // thread2 run // notify t1 // thread2 wait // thread1 run // notify t2 //不斷循環 }
Java Thread常用方法
Thread#yield():
執行此方法會向系統線程調度器(Schelduler)發出一個暗示,告訴其當前JAVA線程打算放棄對CPU的使用,但該暗示,有可能被調度器忽略。使用該方法,可以防止線程對CPU的過度使用,提高系統性能。
Thread#sleep(time)或Thread.sleep(time, nanos):
使當前線程進入休眠階段,狀態變為:TIME_WAITING
Thread.interrupt():
中斷當前線程的執行,允許當前線程對自身進行中斷,否則將會校驗調用方線程是否有對該線程的權限。
如果當前線程因被調用Object#wait(),Object#wait(long, int), 或者線程本身的join(), join(long),sleep()處於阻塞狀態中,此時調用interrupt方法會使拋出InterruptedException,而且線程的阻塞狀態將會被清除。
Thread#interrupted(),返回true或者false:
查看當前線程是否處於中斷狀態,這個方法比較特殊之處在於,如果調用成功,會將當前線程的interrupt status清除。所以如果連續2次調用該方法,第二次將返回false。
Thread.isInterrupted(),返回true或者false:
與上面方法相同的地方在於,該方法返回當前線程的中斷狀態。不同的地方在於,它不會清除當前線程的interrupt status狀態。
Thread#join(),Thread#join(time):
A線程調用B線程的join()方法,將會使A等待B執行,直到B線程終止。如果傳入time參數,將會使A等待B執行time的時間,如果time時間到達,將會切換進A線程,繼續執行A線程。
構造方法和守護線程
構造方法
Thread類中不同的構造方法接受如下參數的不同組合:
一個Runnable對象,這種情況下,Thread.start方法將會調用對應Runnable對象的run方法。如果沒有提供Runnable對象,那么就會立即得到一個Thread.run的默認實現。 一個作為線程標識名的String字符串,該標識在跟蹤和調試過程中會非常有用,除此別無它用。 線程組(ThreadGroup),用來放置新創建的線程,如果提供的ThreadGroup不允許被訪問,那么就會拋出一個SecurityException 。 Thread對象擁有一個守護(daemon)標識屬性,這個屬性無法在構造方法中被賦值,但是可以在線程啟動之前設置該屬性(通過setDaemon方法)。 當程序中所有的非守護線程都已經終止,調用setDaemon方法可能會導致虛擬機粗暴的終止線程並退出。 isDaemon方法能夠返回該屬性的值。守護狀態的作用非常有限,即使是后台線程在程序退出的時候也經常需要做一些清理工作。 (daemon的發音為”day-mon”,這是系統編程傳統的遺留,系統守護進程是一個持續運行的進程,比如打印機隊列管理,它總是在系統中運行。)
啟動線程的方式和isAlive方法
啟動線程
調用start方法會觸發Thread實例以一個新的線程啟動其run方法。新線程不會持有調用線程的任何同步鎖。
當一個線程正常地運行結束或者拋出某種未檢測的異常(比如,運行時異常(RuntimeException),錯誤(ERROR) 或者其子類)線程就會終止。
當線程終止之后,是不能被重新啟動的。在同一個Thread上調用多次start方法會拋出InvalidThreadStateException異常。
如果線程已經啟動但是還沒有終止,那么調用isAlive方法就會返回true.即使線程由於某些原因處於阻塞(Blocked)狀態該方法依然返回true。
如果線程已經被取消(cancelled),那么調用其isAlive在什么時候返回false就因各Java虛擬機的實現而異了。沒有方法可以得知一個處於非活動狀態的線程是否已經被啟動過了。
Java多線程優先級
Java的線程實現基本上都是內核級線程的實現,所以Java線程的具體執行還取決於操作系統的特性。
Java虛擬機為了實現跨平台(不同的硬件平台和各種操作系統)的特性,Java語言在線程調度與調度公平性上未作出任何的承諾,甚至都不會嚴格保證線程會被執行。但是Java線程卻支持優先級的方法,這些方法會影響線程的調度:
每個線程都有一個優先級,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別為1和10)
默認情況下,新創建的線程都擁有和創建它的線程相同的優先級。main方法所關聯的初始化線程擁有一個默認的優先級,這個優先級是Thread.NORM_PRIORITY (5).
線程的當前優先級可以通過getPriority方法獲得。
線程的優先級可以通過setPriority方法來動態的修改,一個線程的最高優先級由其所在的線程組限定。
Java多線程面試題
這篇文章主要是對多線程的問題進行總結的,因此羅列了40個多線程的問題。
這些多線程的問題,有些來源於各大網站、有些來源於自己的思考。可能有些問題網上有、可能有些問題對應的答案也有、也可能有些各位網友也都看過,但是本文寫作的重心就是所有的問題都會按照自己的理解回答一遍,不會去看網上的答案,因此可能有些問題講的不對,能指正的希望大家不吝指教。
1、多線程有什么用?
一個可能在很多人看來很扯淡的一個問題:我會用多線程就好了,還管它有什么用?在我看來,這個回答更扯淡。所謂"知其然知其所以然","會用"只是"知其然","為什么用"才是"知其所以然",只有達到"知其然知其所以然"的程度才可以說是把一個知識點運用自如。OK,下面說說我對這個問題的看法:
1)發揮多核CPU的優勢
隨着工業的進步,現在的筆記本、台式機乃至商用的應用服務器至少也都是雙核的,4核、8核甚至16核的也都不少見,如果是單線程的程序,那么在雙核CPU上就浪費了50%,在4核CPU上就浪費了75%。單核CPU上所謂的"多線程"那是假的多線程,同一時間處理器只會處理一段邏輯,只不過線程之間切換得比較快,看着像多個線程"同時"運行罷了。多核CPU上的多線程才是真正的多線程,它能讓你的多段邏輯同時工作,多線程,可以真正發揮出多核CPU的優勢來,達到充分利用CPU的目的。
2)防止阻塞
從程序運行效率的角度來看,單核CPU不但不會發揮出多線程的優勢,反而會因為在單核CPU上運行多線程導致線程上下文的切換,而降低程序整體的效率。但是單核CPU我們還是要應用多線程,就是為了防止阻塞。試想,如果單核CPU使用單線程,那么只要這個線程阻塞了,比方說遠程讀取某個數據吧,對端遲遲未返回又沒有設置超時時間,那么你的整個程序在數據返回回來之前就停止運行了。多線程可以防止這個問題,多條線程同時運行,哪怕一條線程的代碼執行讀取數據阻塞,也不會影響其它任務的執行。
3)便於建模
這是另外一個沒有這么明顯的優點了。假設有一個大的任務A,單線程編程,那么就要考慮很多,建立整個程序模型比較麻煩。但是如果把這個大的任務A分解成幾個小任務,任務B、任務C、任務D,分別建立程序模型,並通過多線程分別運行這幾個任務,那就簡單很多了。
2、創建線程的方式
比較常見的一個問題了,一般就是兩種:
1)繼承Thread類
2)實現Runnable接口
至於哪個好,不用說肯定是后者好,因為實現接口的方式比繼承類的方式更靈活,也能減少程序之間的耦合度,面向接口編程也是設計模式6大原則的核心。
3、start()方法和run()方法的區別
只有調用了start()方法,才會表現出多線程的特性,不同線程的run()方法里面的代碼交替執行。如果只是調用run()方法,那么代碼還是同步執行的,必須等待一個線程的run()方法里面的代碼全部執行完畢之后,另外一個線程才可以執行其run()方法里面的代碼。
4、Runnable接口和Callable接口的區別
有點深的問題了,也看出一個Java程序員學習知識的廣度。
Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的代碼而已;Callable接口中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取異步執行的結果。
這其實是很有用的一個特性,因為多線程相比單線程更難、更復雜的一個重要原因就是因為多線程充滿着未知性,某條線程是否執行了?某條線程執行了多久?某條線程執行的時候我們期望的數據是否已經賦值完畢?無法得知,我們能做的只是等待這條多線程的任務執行完畢而已。而Callable+Future/FutureTask卻可以獲取多線程運行的結果,可以在等待時間太長沒獲取到需要的數據的情況下取消該線程的任務,真的是非常有用。
5、CyclicBarrier和CountDownLatch的區別
兩個看上去有點像的類,都在java.util.concurrent下,都可以用來表示代碼運行到某個點上,二者的區別在於:
1)CyclicBarrier的某個線程運行到某個點上之后,該線程即停止運行,直到所有的線程都到達了這個點,所有線程才重新運行;CountDownLatch則不是,某線程運行到某個點上之后,只是給某個數值-1而已,該線程繼續運行。
2)CyclicBarrier只能喚起一個任務,CountDownLatch可以喚起多個任務。
3) CyclicBarrier可重用,CountDownLatch不可重用,計數值為0該CountDownLatch就不可再用了。
6、volatile關鍵字的作用
一個非常重要的問題,是每個學習、應用多線程的Java程序員都必須掌握的。理解volatile關鍵字的作用的前提是要理解Java內存模型,這里就不講Java內存模型了,可以參見第31點,volatile關鍵字的作用主要有兩個:
1)多線程主要圍繞可見性和原子性兩個特性而展開,使用volatile關鍵字修飾的變量,保證了其在多線程之間的可見性,即每次讀取到volatile變量,一定是最新的數據。
2)代碼底層執行不像我們看到的高級語言----Java程序這么簡單,它的執行是Java代碼-->字節碼-->根據字節碼執行對應的C/C++代碼-->C/C++代碼被編譯成匯編語言-->和硬件電路交互,現實中,為了獲取更好的性能JVM可能會對指令進行重排序,多線程下可能會出現一些意想不到的問題。使用volatile則會對禁止語義重排序,當然這也一定程度上降低了代碼執行效率。
從實踐角度而言,volatile的一個重要作用就是和CAS結合,保證了原子性,詳細的可以參見java.util.concurrent.atomic包下的類,比如AtomicInteger,更多詳情請點擊這里進行學習。
7、什么是線程安全
又是一個理論的問題,各式各樣的答案有很多,我給出一個個人認為解釋地最好的:如果你的代碼在多線程下執行和在單線程下執行永遠都能獲得一樣的結果,那么你的代碼就是線程安全的。
這個問題有值得一提的地方,就是線程安全也是有幾個級別的:
1)不可變
像String、Integer、Long這些,都是final類型的類,任何一個線程都改變不了它們的值,要改變除非新創建一個,因此這些不可變對象不需要任何同步手段就可以直接在多線程環境下使用
2)絕對線程安全
不管運行時環境如何,調用者都不需要額外的同步措施。要做到這一點通常需要付出許多額外的代價,Java中標注自己是線程安全的類,實際上絕大多數都不是線程安全的,不過絕對線程安全的類,Java中也有,比方說CopyOnWriteArrayList、CopyOnWriteArraySet
3)相對線程安全
相對線程安全也就是我們通常意義上所說的線程安全,像Vector這種,add、remove方法都是原子操作,不會被打斷,但也僅限於此,如果有個線程在遍歷某個Vector、有個線程同時在add這個Vector,99%的情況下都會出現ConcurrentModificationException,也就是fail-fast機制。
4)線程非安全
這個就沒什么好說的了,ArrayList、LinkedList、HashMap等都是線程非安全的類,點擊這里了解為什么不安全。
8、Java中如何獲取到線程dump文件
死循環、死鎖、阻塞、頁面打開慢等問題,打線程dump是最好的解決問題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:
1)獲取到線程的pid,可以通過使用jps命令,在Linux環境下還可以使用ps -ef | grep java
2)打印線程堆棧,可以通過使用jstack pid命令,在Linux環境下還可以使用kill -3 pid
另外提一點,Thread類提供了一個getStackTrace()方法也可以用於獲取線程堆棧。這是一個實例方法,因此此方法是和具體線程實例綁定的,每次獲取獲取到的是具體某個線程當前運行的堆棧。
9、一個線程如果出現了運行時異常會怎么樣
如果這個異常沒有被捕獲的話,這個線程就停止執行了。另外重要的一點是:如果這個線程持有某個某個對象的監視器,那么這個對象監視器會被立即釋放
10、如何在兩個線程之間共享數據
通過在線程之間共享對象就可以了,然后通過wait/notify/notifyAll、await/signal/signalAll進行喚起和等待,比方說阻塞隊列BlockingQueue就是為線程之間共享數據而設計的
11、sleep方法和wait方法有什么區別
這個問題常問,sleep方法和wait方法都可以用來放棄CPU一定的時間,不同點在於如果線程持有某個對象的監視器,sleep方法不會放棄這個對象的監視器,wait方法會放棄這個對象的監視器
12、生產者消費者模型的作用是什么
這個問題很理論,但是很重要:
1)通過平衡生產者的生產能力和消費者的消費能力來提升整個系統的運行效率,這是生產者消費者模型最重要的作用
2)解耦,這是生產者消費者模型附帶的作用,解耦意味着生產者和消費者之間的聯系少,聯系越少越可以獨自發展而不需要收到相互的制約
13、ThreadLocal有什么用
簡單說ThreadLocal就是一種以空間換時間的做法,在每個Thread里面維護了一個以開地址法實現的ThreadLocal.ThreadLocalMap,把數據進行隔離,數據不共享,自然就沒有線程安全方面的問題了
14、為什么wait()方法和notify()/notifyAll()方法要在同步塊中被調用
這是JDK強制的,wait()方法和notify()/notifyAll()方法在調用前都必須先獲得對象的鎖
15、wait()方法和notify()/notifyAll()方法在放棄對象監視器時有什么區別
wait()方法和notify()/notifyAll()方法在放棄對象監視器的時候的區別在於:wait()方法立即釋放對象監視器,notify()/notifyAll()方法則會等待線程剩余代碼執行完畢才會放棄對象監視器。
16、為什么要使用線程池
避免頻繁地創建和銷毀線程,達到線程對象的重用。另外,使用線程池還可以根據項目靈活地控制並發的數目。點擊這里學習線程池詳解。
17、怎么喚醒一個阻塞的線程
如果線程是因為調用了wait()、sleep()或者join()方法而導致的阻塞,可以中斷線程,並且通過拋出InterruptedException來喚醒它;如果線程遇到了IO阻塞,無能為力,因為IO是操作系統實現的,Java代碼並沒有辦法直接接觸到操作系統。
18、不可變對象對多線程有什么幫助
前面有提到過的一個問題,不可變對象保證了對象的內存可見性,對不可變對象的讀取不需要進行額外的同步手段,提升了代碼執行效率。
19、什么是多線程的上下文切換
多線程的上下文切換是指CPU控制權由一個已經正在運行的線程切換到另外一個就緒並等待獲取CPU執行權的線程的過程。
20、線程類的構造方法、靜態塊是被哪個線程調用的
這是一個非常刁鑽和狡猾的問題。請記住:線程類的構造方法、靜態塊是被new這個線程類所在的線程所調用的,而run方法里面的代碼才是被線程自身所調用的。
如果說上面的說法讓你感到困惑,那么我舉個例子,假設Thread2中new了Thread1,main函數中new了Thread2,那么:
1)Thread2的構造方法、靜態塊是main線程調用的,Thread2的run()方法是Thread2自己調用的
2)Thread1的構造方法、靜態塊是Thread2調用的,Thread1的run()方法是Thread1自己調用的
21、高並發、任務執行時間短的業務怎樣使用線程池?並發不高、任務執行時間長的業務怎樣使用線程池?並發高、業務執行時間長的業務怎樣使用線程池?
這是我在並發編程網上看到的一個問題,把這個問題放在最后一個,希望每個人都能看到並且思考一下,因為這個問題非常好、非常實際、非常專業。關於這個問題,個人看法是:
1)高並發、任務執行時間短的業務,線程池線程數可以設置為CPU核數+1,減少線程上下文的切換
2)並發不高、任務執行時間長的業務要區分開看:
a)假如是業務時間長集中在IO操作上,也就是IO密集型的任務,因為IO操作並不占用CPU,所以不要讓所有的CPU閑下來,可以加大線程池中的線程數目,讓CPU處理更多的業務
b)假如是業務時間長集中在計算操作上,也就是計算密集型任務,這個就沒辦法了,和(1)一樣吧,線程池中的線程數設置得少一些,減少線程上下文的切換
c)並發高、業務執行時間長,解決這種類型任務的關鍵不在於線程池而在於整體架構的設計,看看這些業務里面某些數據是否能做緩存是第一步,增加服務器是第二步,至於線程池的設置,設置參考其他有關線程池的文章。最后,業務執行時間長的問題,也可能需要分析一下,看看能不能使用中間件對任務進行拆分和解耦。