導讀
世界上只有兩種物質:高效率和低效率;世界上只有兩種人:高效率的人和低效率的人。——蕭伯納
同理,世界上只有兩種代碼:高效代碼和低效代碼;世界上只有兩種人:編寫高效代碼的人和編寫低效代碼的人。如何編寫高效代碼,是每個研發團隊都面臨的一個重大問題。所以,作者根據實際經驗,查閱了大量資料,總結了"Java高效代碼50例",讓每一個Java程序員都能編寫出"高效代碼"。
1.常量&變量
1.1.直接賦值常量值,禁止聲明新對象
直接賦值常量值,只是創建了一個對象引用,而這個對象引用指向常量值。
反例:
Long?i?=?new?Long(1L); String?s?=?new?String("abc");
正例:
Long?i?=?1L; String?s?=?"abc";
1.2.當成員變量值無需改變時,盡量定義為靜態常量
在類的每個對象實例中,每個成員變量都有一份副本,而成員靜態常量只有一份實例。
反例:
public?class?HttpConnection?{ ????private?final?long?timeout?=?5L; ????... }
正例:
public?class?HttpConnection?{ ????private?static?final?long?TIMEOUT?=?5L; ????... }
1.3.盡量使用基本數據類型,避免自動裝箱和拆箱
Java 中的基本數據類型double、float、long、int、short、char、boolean,分別對應包裝類Double、Float、Long、Integer、Short、Character、Boolean。 JVM支持基本類型與對應包裝類的自動轉換,被稱為自動裝箱和拆箱。裝箱和拆箱都是需要CPU和內存資源的,所以應盡量避免使用自動裝箱和拆箱。
反例:
Integer?sum?=?0; int[]?values?=?...; for?(int?value?:?values)?{ ????sum?+=?value;?//?相當于result?=?Integer.valueOf(result.intValue()?+?value); }
正例:
int?sum?=?0; int[]?values?=?...; for?(int?value?:?values)?{ ????sum?+=?value; }
1.4.如果變量的初值會被覆蓋,就沒有必要給變量賦初值
反例:
List
正例:
List
1.5.盡量使用函數內的基本類型臨時變量
在函數內,基本類型的參數和臨時變量都保存在棧(Stack)中,訪問速度較快;對象類型的參數和臨時變量的引用都保存在棧(Stack)中,內容都保存在堆(Heap)中,訪問速度較慢。在類中,任何類型的成員變量都保存在堆(Heap)中,訪問速度較慢。
反例:
public?final?class?Accumulator?{ ????private?double?result?=?0.0D; ????public?void?addAll(@NonNull?double[]?values)?{ ????????for(double?value?:?values)?{ ????????????result?+=?value; ????????} ????} ????... }
正例:
public?final?class?Accumulator?{ ????private?double?result?=?0.0D; ????public?void?addAll(@NonNull?double[]?values)?{ ????????double?sum?=?0.0D; ????????for(double?value?:?values)?{ ????????????sum?+=?value; ????????} ????????result?+=?sum; ????} ????... }
1.6.盡量不要在循環體外定義變量
在老版JDK中,建議“盡量不要在循環體內定義變量”,但是在新版的JDK中已經做了優化。通過對編譯后的字節碼分析,變量定義在循環體外和循環體內沒有本質的區別,運行效率基本上是一樣的。反而,根據“ 局部變量作用域最小化 ”原則,變量定義在循環體內更科學更便于維護,避免了延長大對象生命周期導致延緩回收問題 。
反例:
UserVO?userVO; List
正例:
List
1.7.不可變的靜態常量,盡量使用非線程安全類
不可變的靜態常量,雖然需要支持多線程訪問,也可以使用非線程安全類。
反例:
public?static?final?Map
正例:
public?static?final?Map
1.8.不可變的成員變量,盡量使用非線程安全類
不可變的成員變量,雖然需要支持多線程訪問,也可以使用非線程安全類。
反例:
@Service public?class?StrategyFactory?implements?InitializingBean?{ ????@Autowired ????private?List
正例:
@Service public?class?StrategyFactory?implements?InitializingBean?{ ????@Autowired ????private?List
2.對象&類
2.1.禁止使用JSON轉化對象
JSON提供把對象轉化為JSON字符串、把JSON字符串轉為對象的功能,于是被某些人用來轉化對象。這種對象轉化方式,雖然在功能上沒有問題,但是在性能上卻存在問題。
反例:
List
正例:
List
2.2.盡量不使用反射賦值對象
用反射賦值對象,主要優點是節省了代碼量,主要缺點卻是性能有所下降。
反例:
List
正例:
List
2.3.采用Lambda表達式替換內部匿名類
對于大多數剛接觸JDK8的同學來說,都會認為Lambda表達式就是匿名內部類的語法糖。實際上, Lambda表達式在大多數虛擬機中采用invokeDynamic指令實現,相對于匿名內部類在效率上會更高一些。
反例:
List
正例:
List
2.4.盡量避免定義不必要的子類
多一個類就需要多一份類加載,所以盡量避免定義不必要的子類。
反例:
public?static?final?Map
正例:
public?static?final?Map
2.5.盡量指定類的final修飾符
為類指定final修飾符,可以讓該類不可以被繼承。如果指定了一個類為final,則該類所有的方法都是final的,Java編譯器會尋找機會內聯所有的final方法。內聯對于提升Java運行效率作用重大,具體可參見Java運行期優化,能夠使性能平均提高50%。
反例:
public?class?DateHelper?{ ????... }
正例:
public?final?class?DateHelper?{ ????... }
注意:使用Spring的AOP特性時,需要對Bean進行動態代理,如果Bean類添加了final修飾,會導致異常。
3.方法
3.1.把跟類成員變量無關的方法聲明成靜態方法
靜態方法的好處就是不用生成類的實例就可以直接調用。靜態方法不再屬于某個對象,而是屬于它所在的類。只需要通過其類名就可以訪問,不需要再消耗資源去反復創建對象。即便在類內部的私有方法,如果沒有使用到類成員變量,也應該聲明為靜態方法。
反例:
public?int?getMonth(Date?date)?{ ????Calendar?calendar?=?Calendar.getInstance(); ????calendar.setTime(date); ????return?calendar.get(Calendar.MONTH)?+?1; }
正例:
public?static?int?getMonth(Date?date)?{ ????Calendar?calendar?=?Calendar.getInstance(); ????calendar.setTime(date); ????return?calendar.get(Calendar.MONTH)?+?1; }
3.2.盡量使用基本數據類型作為方法參數類型,避免不必要的裝箱、拆箱和空指針判斷
反例:
public?static?double?sum(Double?value1,?Double?value2)?{ ????double?double1?=?Objects.isNull(value1)???0.0D?:?value1; ????double?double2?=?Objects.isNull(value2)???0.0D?:?value2; ????return?double1?+?double2; } double?result?=?sum(1.0D,?2.0D);
正例:
public?static?double?sum(double?value1,?double?value2)?{ ????return?value1?+?value2; } double?result?=?sum(1.0D,?2.0D);
3.3.盡量使用基本數據類型作為方法返回值類型,避免不必要的裝箱、拆箱和空指針判斷
在JDK類庫的方法中,很多方法返回值都采用了基本數據類型,首先是為了避免不必要的裝箱和拆箱,其次是為了避免返回值的空指針判斷。比如:Collection.isEmpty()和Map.size()。
反例:
public?static?Boolean?isValid(UserDO?user)?{ ????if?(Objects.isNull(user))?{ ????????return?false; ????} ????return?Boolean.TRUE.equals(user.getIsValid()); } //?調用代碼 UserDO?user?=?...; Boolean?isValid?=?isValid(user); if?(Objects.nonNull(isValid)?&&?isValid.booleanValue())?{? ????... }
正例:
public?static?boolean?isValid(UserDO?user)?{ ????if?(Objects.isNull(user))?{ ????????return?false; ????} ????return?Boolean.TRUE.equals(user.getIsValid()); } //?調用代碼 UserDO?user?=?...; if?(isValid(user))?{ ????... }
3.4.協議方法參數值非空,避免不必要的空指針判斷
協議編程,可以@NonNull和@Nullable標注參數,是否遵循全憑調用者自覺。
反例:
public?static?boolean?isValid(UserDO?user)?{ ????if?(Objects.isNull(user))?{ ????????return?false; ????} ????return?Boolean.TRUE.equals(user.getIsValid()); }
正例:
public?static?boolean?isValid(@NonNull?UserDO?user)?{ ????return?Boolean.TRUE.equals(user.getIsValid()); }
3.5.協議方法返回值非空,避免不必要的空指針判斷
協議編程,可以@NonNull和@Nullable標注參數,是否遵循全憑實現者自覺。
反例:
//?定義接口 public?interface?OrderService?{ ????public?List
正例:
//?定義接口 public?interface?OrderService?{ ????@NonNull ????public?List
3.6.被調用方法已支持判空處理,調用方法無需再進行判空處理
反例:
UserDO?user?=?null; if?(StringUtils.isNotBlank(value))?{ ????user?=?JSON.parseObject(value,?UserDO.class); }
正例:
UserDO?user?=?JSON.parseObject(value,?UserDO.class);
3.7.盡量避免不必要的函數封裝
方法調用會引起入棧和出棧,導致消耗更多的CPU和內存,應當盡量避免不必要的函數封裝。當然,為了使代碼更簡潔、更清晰、更易維護,增加一定的方法調用所帶來的性能損耗是值得的。
反例:
//?函數封裝 public?static?boolean?isVip(Boolean?isVip)?{ ????return?Boolean.TRUE.equals(isVip); } //?使用代碼 boolean?isVip?=?isVip(user.getVip());
正例:
boolean?isVip?=?Boolean.TRUE.equals(user.getVip());
3.8.盡量指定方法的final修飾符
方法指定final修飾符,可以讓方法不可以被重寫,Java編譯器會尋找機會內聯所有的final方法。內聯對于提升Java運行效率作用重大,具體可參見Java運行期優化,能夠使性能平均提高50%。
注意:所有的private方法會隱式地被指定final修飾符,所以無須再為其指定final修飾符。
反例:
public?class?Rectangle?{ ????... ????public?double?area()?{ ????????... ????} }
正例:
public?class?Rectangle?{ ????... ????public?final?double?area()?{ ????????... ????} }
注意:使用Spring的AOP特性時,需要對Bean進行動態代理,如果方法添加了final修飾,將不會被代理。
4.表達式
4.1.盡量減少方法的重復調用
反例:
List
正例:
List
4.2.盡量避免不必要的方法調用
反例:
List
正例:
List
4.3.盡量使用移位來代替正整數乘除
用移位操作可以極大地提高性能。對于乘除2^n(n為正整數)的正整數計算,可以用移位操作來代替。
反例:
int?num1?=?a?*?4; int?num2?=?a?/?4;
正例:
int?num1?=?a?<2; int?num2?=?a?>>?2;
4.4.提取公共表達式,避免重復計算
提取公共表達式,只計算一次值,然后重復利用值。
反例:
double?distance?=?Math.sqrt((x2?-?x1)?*?(x2?-?x1)?+?(y2?-?y1)?*?(y2?-?y1));
正例:
double?dx?=?x2?-?x1; double?dy?=?y2?-?y1; double?distance?=?Math.sqrt(dx?*?dx?+?dy?*?dy); 或 double?distance?=?Math.sqrt(Math.pow(x2?-?x1,?2)?+?Math.pow(y2?-?y1,?2));
4.5.盡量不在條件表達式中用!取反
使用!取反會多一次計算,如果沒有必要則優化掉。
反例:
if?(!(a?>=?10))?{ ????...?//?條件處理1 }?else?{ ????...?//?條件處理2 }
正例:
if?(a?10)?{ ????...?//?條件處理1 }?else?{ ????...?//?條件處理2 }
4.6.對于多常量選擇分支,盡量使用switch語句而不是if-else語句
if-else語句,每個if條件語句都要加裝計算,直到if條件語句為true為止。switch語句進行了跳轉優化,Java中采用tableswitch或lookupswitch指令實現,對于多常量選擇分支處理效率更高。經過試驗證明:在每個分支出現概率相同的情況下,低于5個分支時if-else語句效率更高,高于5個分支時switch語句效率更高。
反例:
if?(i?==?1)?{ ????...;?//?分支1 }?else?if?(i?==?2)?{ ????...;?//?分支2 }?else?if?(i?==?...)?{ ????...;?//?分支n }?else?{ ????...;?//?分支n+1 }
正例:
switch?(i)?{ ????case?1?: ????????...?//?分支1 ????????break; ????case?2?: ????????...?//?分支2 ????????break; ????case?...?: ????????...?//?分支n ????????break; ????default?: ????????...?//?分支n+1 ????????break; }
備注:如果業務復雜,可以采用Map實現策略模式。
5.字符串
5.1.盡量不要使用正則表達式匹配
正則表達式匹配效率較低,盡量使用字符串匹配操作。
反例:
String?source?=?"a::1,b::2,c::3,d::4"; String?target?=?source.replaceAll("::",?"="); Stringp[]?targets?=?source.spit("::");
正例:
String?source?=?"a::1,b::2,c::3,d::4"; String?target?=?source.replace("::",?"="); Stringp[]?targets?=?StringUtils.split(source,?"::");
5.2.盡量使用字符替換字符串
字符串的長度不確定,而字符的長度固定為1,查找和匹配的效率自然提高了。
反例:
String?source?=?"a:1,b:2,c:3,d:4"; int?index?=?source.indexOf(":"); String?target?=?source.replace(":",?"=");
正例:
String?source?=?"a:1,b:2,c:3,d:4"; int?index?=?source.indexOf(':'); String?target?=?source.replace(':',?'=');
5.3.盡量使用StringBuilder進行字符串拼接
String是final類,內容不可修改,所以每次字符串拼接都會生成一個新對象。StringBuilder在初始化時申請了一塊內存,以后的字符串拼接都在這塊內存中執行,不會申請新內存和生成新對象。
反例:
String?s?=?""; for?(int?i?=?0;?i?10;?i++)?{ ????if?(i?!=?0)?{ ????????s?+=?','; ????} ????s?+=?i; }
正例:
StringBuilder?sb?=?new?StringBuilder(128); for?(int?i?=?0;?i?10;?i++)?{ ????if?(i?!=?0)?{ ????????sb.append(','); ????} ????sb.append(i); }
5.4.不要使用""+轉化字符串
使用""+進行字符串轉化,使用方便但是效率低,建議使用String.valueOf.
反例:
int?i?=?12345; String?s?=?""?+?i;
正例:
int?i?=?12345; String?s?=?String.valueOf(i);
6.數組
6.1.不要使用循環拷貝數組,盡量使用System.arraycopy拷貝數組
推薦使用System.arraycopy拷貝數組,也可以使用Arrays.copyOf拷貝數組。
反例:
int[]?sources?=?new?int[]?{1,?2,?3,?4,?5}; int[]?targets?=?new?int[sources.length]; for?(int?i?=?0;?i?
正例:
int[]?sources?=?new?int[]?{1,?2,?3,?4,?5}; int[]?targets?=?new?int[sources.length]; System.arraycopy(sources,?0,?targets,?0,?targets.length);
6.2.集合轉化為類型T數組時,盡量傳入空數組T[0]
將集合轉換為數組有2種形式:toArray(new T[n])和toArray(new T[0])。在舊的Java版本中,建議使用toArray(new T[n]),因為創建數組時所需的反射調用非常慢。在OpenJDK6后,反射調用是內在的,使得性能得以提高,toArray(new T[0])比toArray(new T[n])效率更高。此外,toArray(new T[n])比toArray(new T[0])多獲取一次列表大小,如果計算列表大小耗時過長,也會導致toArray(new T[n])效率降低。
反例:
List
正例:
List
建議:集合應該提供一個toArray(Class
6.3.集合轉化為Object數組時,盡量使用toArray()方法
轉化Object數組時,沒有必要使用toArray[new Object[0]],可以直接使用toArray()。避免了類型的判斷,也避免了空數組的申請,所以效率會更高。
反例:
List
正例:
List
7.集合
7.1.初始化集合時,盡量指定集合大小
Java集合初始化時都會指定一個默認大小,當默認大小不再滿足數據需求時就會擴容,每次擴容的時間復雜度有可能是O(n)。所以,盡量指定預知的集合大小,就能避免或減少集合的擴容次數。
反例:
List
正例:
List
7.2.不要使用循環拷貝集合,盡量使用JDK提供的方法拷貝集合
JDK提供的方法可以一步指定集合的容量,避免多次擴容浪費時間和空間。同時,這些方法的底層也是調用System.arraycopy方法實現,進行數據的批量拷貝效率更高。
反例:
List
正例:
List
7.3.盡量使用Arrays.asList轉化數組為列表
原理與"不要使用循環拷貝集合,盡量使用JDK提供的方法拷貝集合"類似。
反例:
List
正例:
List
7.4.直接迭代需要使用的集合
直接迭代需要使用的集合,無需通過其它操作獲取數據。
反例:
Map
正例:
Map
7.5.不要使用size方法檢測空,必須使用isEmpty方法檢測空
使用size方法來檢測空邏輯上沒有問題,但使用isEmpty方法使得代碼更易讀,并且可以獲得更好的性能。任何isEmpty方法實現的時間復雜度都是O(1),但是某些size方法實現的時間復雜度有可能是O(n)。
反例:
List
正例:
List
7.6.非隨機訪問的List,盡量使用迭代代替隨機訪問
對于列表,可分為隨機訪問和非隨機訪問兩類,可以用是否實現RandomAccess接口判斷。隨機訪問列表,直接通過get獲取數據不影響效率。而非隨機訪問列表,通過get獲取數據效率極低。
反例:
LinkedList
正例:
LinkedList
其實,不管列表支不支持隨機訪問,都應該使用迭代進行遍歷。
7.7.盡量使用HashSet判斷值存在
在Java集合類庫中,List的contains方法普遍時間復雜度是O(n),而HashSet的時間復雜度為O(1)。如果需要頻繁調用contains方法查找數據,可以先將List轉換成HashSet。
反例:
List
正例:
Set
7.8.避免先判斷存在再進行獲取
如果需要先判斷存在再進行獲取,可以直接獲取并判斷空,從而避免了二次查找操作。
反例:
public?static?UserVO?transUser(UserDO?user,?Map
正例:
public?static?UserVO?transUser(UserDO?user,?Map
8.異常
8.1.直接捕獲對應的異常
直接捕獲對應的異常,避免用instanceof判斷,效率更高代碼更簡潔。
反例:
try?{ ????saveData(); }?catch?(Exception?e)?{ ????if?(e?instanceof?IOException)?{ ????????log.error("保存數據IO異常",?e); ????}?else?{ ????????log.error("保存數據其它異常",?e); ????} }
正例:
try?{ ????saveData(); }?catch?(IOException?e)?{ ????log.error("保存數據IO異常",?e); }?catch?(Exception?e)?{ ????log.error("保存數據其它異常",?e); }
8.2.盡量避免在循環中捕獲異常
當循環體拋出異常后,無需循環繼續執行時,沒有必要在循環體中捕獲異常。因為,過多的捕獲異常會降低程序執行效率。
反例:
public?Double?sum(List
正例:
public?Double?sum(List
8.3.禁止使用異常控制業務流程
相對于條件表達式,異常的處理效率更低。
反例:
public?static?boolean?isValid(UserDO?user)?{ ????try?{ ????????return?Boolean.TRUE.equals(user.getIsValid()); ????}?catch(NullPointerException?e)?{ ????????return?false; ????} }
正例:
public?static?boolean?isValid(UserDO?user)?{ ????if?(Objects.isNull(user))?{ ????????return?false; ????} ????return?Boolean.TRUE.equals(user.getIsValid()); }
9.緩沖區
9.1.初始化時盡量指定緩沖區大小
初始化時,指定緩沖區的預期容量大小,避免多次擴容浪費時間和空間。
反例:
StringBuffer?buffer?=?new?StringBuffer(); StringBuilder?builder?=?new?StringBuilder();
正例:
StringBuffer?buffer?=?new?StringBuffer(1024); StringBuilder?builder?=?new?StringBuilder(1024);
9.2.盡量重復使用同一緩沖區
針對緩沖區,Java虛擬機需要花時間生成對象,還要花時間進行垃圾回收處理。所以,盡量重復利用緩沖區。
反例:
StringBuilder?builder1?=?new?StringBuilder(128); builder1.append("update?t_user?set?name?=?'").append(userName).append("'?where?id?=?").append(userId); statement.executeUpdate(builder1.toString()); StringBuilder?builder2?=?new?StringBuilder(128); builder2.append("select?id,?name?from?t_user?where?id?=?").append(userId); ResultSet?resultSet?=?statement.executeQuery(builder2.toString()); ...
正例:
StringBuilder?builder?=?new?StringBuilder(128); builder.append("update?t_user?set?name?=?'").append(userName).append("'?where?id?=?").append(userId); statement.executeUpdate(builder.toString()); builder.setLength(0); builder.append("select?id,?name?from?t_user?where?id?=?").append(userId); ResultSet?resultSet?=?statement.executeQuery(builder.toString()); ...
其中,使用setLength方法讓緩沖區重新從0開始。
9.3.盡量設計使用同一緩沖區
為了提高程序運行效率,在設計上盡量使用同一緩沖區。
反例:
//?轉化XML(UserDO) public?static?String?toXml(UserDO?user)?{ ????StringBuilder?builder?=?new?StringBuilder(128); ????builder.append("
正例:
//?轉化XML(UserDO) public?static?void?toXml(StringBuilder?builder,?UserDO?user)?{ ????builder.append("
去掉每個轉化方法中的緩沖區申請,申請一個緩沖區給每個轉化方法使用。從時間上來說,節約了大量緩沖區的申請釋放時間;從空間上來說,節約了大量緩沖區的臨時存儲空間。
9.4.盡量使用緩沖流減少IO操作
使用緩沖流BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream等,可以大幅減少IO次數并提升IO速度。
反例:
try?(FileInputStream?input?=?new?FileInputStream("a"); ????FileOutputStream?output?=?new?FileOutputStream("b"))?{ ????int?size?=?0; ????byte[]?temp?=?new?byte[1024]; ????while?((size?=?input.read(temp))?!=?-1)?{ ????????output.write(temp,?0,?size); ????} }?catch?(IOException?e)?{ ????log.error("復制文件異常",?e); }
正例:
try?(BufferedInputStream?input?=?new?BufferedInputStream(new?FileInputStream("a")); ????BufferedOutputStream?output?=?new?BufferedOutputStream(new?FileOutputStream("b")))?{ ????int?size?=?0; ????byte[]?temp?=?new?byte[1024]; ????while?((size?=?input.read(temp))?!=?-1)?{ ????????output.write(temp,?0,?size); ????} }?catch?(IOException?e)?{ ????log.error("復制文件異常",?e); }
其中,可以根據實際情況手動指定緩沖流的大小,把緩沖流的緩沖作用發揮到最大。
10.線程
10.1.在單線程中,盡量使用非線程安全類
使用非線程安全類,避免了不必要的同步開銷。
反例:
StringBuffer?buffer?=?new?StringBuffer(128); buffer.append("select?*?from?").append(T_USER).append("?where?id?=??");
正例:
StringBuilder?buffer?=?new?StringBuilder(128); buffer.append("select?*?from?").append(T_USER).append("?where?id?=??");
10.2.在多線程中,盡量使用線程安全類
使用線程安全類,比自己實現的同步代碼更簡潔更高效。
反例:
private?volatile?int?counter?=?0; public?void?access(Long?userId)?{ ????synchronized?(this)?{ ????????counter++; ????} ????... }
正例:
private?final?AtomicInteger?counter?=?new?AtomicInteger(0); public?void?access(Long?userId)?{ ????counter.incrementAndGet(); ????... }
10.3.盡量減少同步代碼塊范圍
在一個方法中,可能只有一小部分的邏輯是需要同步控制的,如果同步控制了整個方法會影響執行效率。所以,盡量減少同步代碼塊的范圍,只對需要進行同步的代碼進行同步。
反例:
private?volatile?int?counter?=?0; public?synchronized?void?access(Long?userId)?{ ????counter++; ????...?//?非同步操作 }
正例:
private?volatile?int?counter?=?0; public?void?access(Long?userId)?{ ????synchronized?(this)?{ ????????counter++; ????} ????...?//?非同步操作 }
10.4.盡量合并為同一同步代碼塊
同步代碼塊是有性能開銷的,如果確定可以合并為同一同步代碼塊,就應該盡量合并為同一同步代碼塊。
反例:
//?處理單一訂單 public?synchronized?handleOrder(OrderDO?order)?{ ????... } //?處理所有訂單 public?void?handleOrder(List
正例:
//?處理單一訂單 public?handleOrder(OrderDO?order)?{ ????... } //?處理所有訂單 public?synchronized?void?handleOrder(List
10.5.盡量使用線程池減少線程開銷
多線程中兩個必要的開銷:線程的創建和上下文切換。采用線程池,可以盡量地避免這些開銷。
反例:
public?void?executeTask(Runnable?runnable)?{ ????new?Thread(runnable).start(); }
正例:
private?static?final?ExecutorService?EXECUTOR_SERVICE?=?Executors.newFixedThreadPool(10); public?void?executeTask(Runnable?runnable)?{ ????executorService.execute(runnable); }
評論
查看更多