最近線上遇到幾個小問題,排查代碼發現基本都是些細節問題,作些總結提示你們不要掉到坑中。前端
1、fastjson的序列化SerializerFeature使用注意 java
咱們都知道Integer、Double、Boolean等包裝類型的字段默認值是null。若是不對這些字段設置值,那麼在反序列化時獲得的相應的值也應該是null。mysql
一、本次服務接口升級後致使調用方業務邏輯判斷失敗,好比Integer type 在升級以前是返回的null,而升級後返回了數字0。究其緣由發現同事在序列化時設置了SerializerFeature.WriteNullNumberAsZero,而致使Number類型(Boolean,Integer,Float,Double等)都轉成了0。sql
二、null屬性不顯示數據庫
咱們先來看一段代碼json
Map <String , Object > map = new HashMap<String , Object>(); map.put("a",1); map.put("b",""); map.put("c",null); String str = JSONObject.toJSONString(map); System.out.println(str);//輸出結果:{"a":1,"b":""}
屬性c,怎麼就憑空消失了哪?這裏不講述緣由,只說明解決方案。vim
解決方案:使用fastjson的SerializerFeature序列化屬性,即JSONObject.toJSONString(Object object, SerializerFeature... features)windows
所以上面的代碼就能夠改寫成後端
String str = JSONObject.toJSONString(map,SerializerFeature.WriteMapNullValue);
咱們經常使用的SerializerFeature:數組
總結:
一、服務接口的測試用例覆蓋率仍是過低
二、對於通用處理工具,在作修改時對工具類的來龍去脈要清楚
2、java.util.List.subList的陷阱
咱們通常都會使用java.util.List中有一個subList方法,返回一個以fromIndex爲起始索引(包含),以toIndex爲終止索引(不包含)的一部分的視圖(List)。
List<E> subList(int fromIndex, int toIndex);
之因此說是視圖,是由於實際上,返回的list是靠原來的list支持的。原來的list和返回的list作的「非結構性修改」(non-structural changes),都會影響到彼此對方。
所謂的「非結構性修改」,是指不涉及到list的大小改變的修改。相反,結構性修改,指改變了list大小的修改。
若是發生結構性修改的是返回的子list,那麼原來的list的大小也會發生變化;
若是發生結構性修改的是原來的list(不包括因爲返回的子list致使的改變),那麼會是拋出一個ConcurrentModificationException。
list.remove(index)
list.subList(int fromIndex, int toIndex).clear();
List<Integer> subList2 = new ArrayList<Integer>(list2.subList(2, list2.size()));
那麼ArrayList的remove的底層是怎麼作的?
AbstractList中有一個屬性modCount,這個屬性是跟蹤list中數據被修改的次數,任何對list的add/remove操做,都將致使modCount++。
在AbstractList中還有一個內部類Itr implements Iterator,Itr是一個list遍歷的工具類。固然list.iterator()方法也是返回Itr對象,在Itr中有一個校驗位屬性expectedModCount;對於一個itr對象,其初始時expectedModCount=modCount。
Iterator是list一個視圖,其最終仍是操做list的存儲結構。在使用iterator遍歷時,remove()操做,會致使modCount++(AbstractList.remove()),可是還有expectedModCount=modCount,即在iterator中remove數據,會帶來expectedModCount與modCount值的同步。
在Iterator遍歷時,next(),remove()方法會校驗expectedModCount與modCount值是否一致,若是不一致,就意味着這list數據在iterator外部被修改,此時iterator遍歷將會形成ConcurrentModificationException.
AbstractLlist不只支持普通的iterator,還支持ListIterator(ArrayList,LinkedList均支持),ListIterator增長了遍歷時雙向遊標能力(previous,next),增長了add方法。add方法和remove方法同樣也作了expectedModCount和modCount一致性校驗.
咱們來看下面四個對list數據刪除的代碼的區別
1) for(int i=0;i<list.size();i++){ list.remove(i); } 2) for(int i=list.size()-1;i>=0;i--){ list.remove(i); } 3) int size = list.size(); for(int i=size-1;i>-1;i--){ list.remove(i); } 4) for(Object i : list){ //若是list中存在多個Object互相equals時,此方法仍然有效.注意list.remove(Object)內部使用了遍歷操做,並使用equals來比較對象並刪除. list.remove(i); } 5) Iterator it = list.iterator() while(it.hasNext()){ it.next(); it.remove(); }
1),2),3)是最普通的遍歷方式,可是在遍歷並有刪除操做時,彷佛它們執行的結果還有些差距,根據座標刪除,
那麼1)實事上只會有一半被刪掉,1)中每刪除一次,計算一次list.size(),可是當前i++,且前端刪除會形成數組結構copy。
2)後端刪除,不會形成copy,每次都是刪除最後一個位置,直至結束
3)由於size沒有從新計算,在刪除一半數據後,拋出IndexOutOfBoundsException
4)/5)正常
3、illegal character: \65279
問題產生的操做過程,同事使用SVN提交java文件,發現有衝突使用UltraEdit進行了修改,從新編譯時卻報了異常
java:[1,0] illegal character: \65279
解決方法
將文件從新保存成UTF-8 無BOM便可。
具體緣由參看高人的解釋
http://blog.csdn.net/shixing_11/article/details/6976900
4、Java線程池任務執行完畢後線程回收的問題
咱們知道ThreadPoolExecutor解決了兩個重要的問題:
一、因爲減小了每一個任務調用的開銷,它們一般能夠在執行大量異步任務時提供加強的性能
二、還能夠提供綁定和管理資源(包括執行任務集時使用的線程)的方法。
當使用java中的ThreadPoolExecutor,給咱們的工做帶來方便的同時,若是不當使用一樣也帶來巨大潛在危險。
最近在review代碼時,發現線程池中的全部任務執行完畢後,線程並無被銷燬。咱們知道初始化ThreadPoolExecutor會有構造參數
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
咱們看下TreadPoolExecutor是怎樣工做的
注意
一、核心線程即core線程,只有當前線程數小於等於corePoolSize時,這時的線程才叫核心線程。
二、在新任務被提交時,若是運行的core線程少於corePoolSize,才建立新core線程。並非一開始就建立corePoolSize個core線程。
三、若是運行的線程多於corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才建立新線程。
按需構造
核心線程最初只是在新任務到達時才被ThreadPoolExecutor建立和啓動的,可是也能夠手動調用方法 prestartCoreThread() 或 prestartAllCoreThreads()來的提早啓動核心線程。
若是構造帶有非空隊列的池,這時則可能但願預先啓動線程。
保持活動時間
若是線程池中當前線程數大於corePoolSize ,則這些多出的線程在空閒時間超過 keepAliveTime 時將會終止。
若是後來線程池中線程變得更爲活動,則能夠建立新的線程。也可使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 動態地更改此參數,若是把值設爲Long.MAX_VALUE TimeUnit.NANOSECONDS 的話,空閒線程不會被回收直到ThreadPoolExecutor爲Terminate。
默認狀況下,此種活動策略只在有多於corePoolSize Threads的線程時纔會應用。可是隻要 keepAliveTime 值非 0,也能夠經過allowCoreThreadTimeOut(boolean) 方法也可將此超時策略應用於核心線程。
注意1:setKeepAliveTime(long, java.util.concurrent.TimeUnit)用於設置空閒線程最長的活動時間,即若是空閒時間超過設定值,就中止該線程,對該線程進行回收。
該策略默認只對非內核線程有用(即當前線程數大於corePoolSize),能夠調用allowCoreThreadTimeOut(boolean)方法將此超時策略擴大到核心線程
注意2:若是把值設爲Long.MAX_VALUE TimeUnit.NANOSECONDS的話,空閒線程不會被回收直到ThreadPoolExecutor爲Terminate。
線程終止
若是ThreadPoolExecutor在程序中沒有任何引用且沒有任何活動線程,線程池也不會自動 shutdown。
若是但願確保回收線程(即便用戶忘記調用 shutdown()),則必須安排未使用的線程最終終止,設置適當保持活動時間,設置 allowCoreThreadTimeOut(boolean)。
關鍵函數
按過去執行已提交任務的順序發起一個有序的關閉,可是不接受新任務。若是已經關閉,則調用沒有其餘做用。
拋出:
SecurityException - 若是安全管理器存在而且關閉此 ExecutorService 可能操做某些不容許調用者修改的線程(由於它沒有 RuntimePermission("modifyThread")),或者安全管理器的 checkAccess 方法拒絕訪問。
嘗試中止全部的活動執行任務、暫停等待任務的處理,並返回等待執行的任務列表。在今後方法返回的任務隊列中排空(移除)這些任務。
並不保證可以中止正在處理的活動執行任務,可是會盡力嘗試。 此實現經過 Thread.interrupt() 取消任務,因此沒法響應中斷的任何任務可能永遠沒法終止。
返回:
從未開始執行的任務的列表。
拋出:
SecurityException - 若是安全管理器存在而且關閉此 ExecutorService
可能操做某些不容許調用者修改的線程(由於它沒有 RuntimePermission("modifyThread")),
或者安全管理器的 checkAccess 方法拒絕訪問。
啓動全部核心線程,使其處於等待工做的空閒狀態。僅當執行新任務時,此操做才重寫默認的啓動核心線程策略。
返回:
已啓動的線程數
若是此池容許核心線程超時和終止,若是在 keepAlive 時間內沒有任務到達,新任務到達時正在替換(若是須要),則返回 true。當返回 true 時,適用於非核心線程的相同的保持活動策略也一樣適用於核心線程。當返回 false(默認值)時,因爲沒有傳入任務,核心線程不會終止。
返回:
若是容許核心線程超時,則返回 true;不然返回 false
若是在保持活動時間內沒有任務到達,新任務到達時正在替換(若是須要),則設置控制核心線程是超時仍是終止的策略。當爲 false(默認值)時,因爲沒有傳入任務,核心線程將永遠不會停止。當爲 true 時,適用於非核心線程的相同的保持活動策略也一樣適用於核心線程。爲了不連續線程替換,保持活動時間在設置爲 true 時必須大於 0。一般應該在主動使用該池前調用此方法。
參數:
value - 若是應該超時,則爲 true;不然爲 false
拋出:
IllegalArgumentException - 若是 value 爲 true 而且當前保持活動時間不大於 0。
咱們瞭解了ThreadPoolExecutor後,確保線程的回收就能夠經過如下方式
// 在allowCoreThreadTimeOut設置爲true時,ThreadPoolExecutor的keepAliveTime參數必須大於0。 executor.allowCoreThreadTimeOut(true);
// 在任務執行完後,調用shutdown方法,將線程池中的空閒線程回收 executor.shutdown();
5、Eclipse下maven項目自動打war包丟失jar包問題解決方法
因爲本地沒有maven倉庫和強大的「牆」,使用maven命令clean package卻打包失敗。沒有辦法使用了Eclipse最原始的Export命令,打好包後發現依賴的jar都沒有打進去,真是一波三折呀。現說下完整的打包過程和解決方法:
一、選擇相應的profile,沒有配置略過
二、.project文件
檢查<buildSpec>節點中是否包含以下節點
<buildCommand> <name> org.eclipse.m2e.core.maven2Builder </name> <arguments></arguments> </buildCommand>
檢查<natures>節點中是否包含以下節點
<nature>org.eclipse.m2e.core.maven2Nature</nature>
三、.classpath文件
檢查是否包含以下節點
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> <attributes> <attribute name="maven.pomderived" value="true"/> <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/> </attributes> </classpathentry>
正由於沒有這個屬性<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>,才致使打的包中沒有jar。
上述三步驟檢查完畢後,從新clean工程,再打包就能夠正常了。
6、Linux系統下忽略代表大小寫
坑爹的DBA將線上的一個數據庫重作後,致使了某些服務拋出了大量的異常
select name,type,updateTime from VIDEO_INFO where 1=1 ExecuteData Table 'MS.VIDEO_INFO' doesn't exist
信息提示找不到代表。
咱們都知道Linux是區分代表大小寫,windows不區分代表大小寫的。DBA重作了新的數據庫後卻忽略設置代表大小寫。
解決方法很簡單:
編輯mysql配置文件:vi /etc/my.cnf
添加:lower_case_table_names=1 一句到文件中。
重啓MySQL,服務一切正常。
小結:重要的事情說一遍,檢查一遍。
7、swap file "*.swp" already exists!
Linux下咱們使用vi或vim對文件編輯, 當打開文件或保存文件可能會出現:
swap file "*.swp" already exists!
[O]pen Read-Only, (E)dit anyway, (R)ecover, (D)elete it, (Q)uit, (A)bort:
緣由:
使用vim編輯文件實際是先copy一份臨時文件並映射到內存給你編輯, 編輯的是臨時文件, 當執行:w後才保存臨時文件到原文件,執行:q後才刪除臨時文件。
每次啓動檢索式否有臨時文件, 有則詢問如何處理,就會出現如上情景。
解決方法:
一、顯示隱藏文件 ll -a或ls -a
二、刪除隱藏文件 rm -f *.swp
8、20880端口被佔用
問題描述:
在部署線上服務時,出現20880端口被佔用而沒法啓動。
問題分析:
20880端口被該服務器上的客戶端隨機選取源端口給佔用掉了。
解決方案:
一、使用net.ipv4.ip_local_port_range參數,規劃出一段端口段預留做爲服務的端口,這種方法是能夠解決當前問題,可是會有個問題,端口使用量減小了,當服務器須要消耗大量的端口號的話,好比反代服務器,就存在瓶頸了。
二、將服務監聽的端口以逗號分隔所有添加到ip_local_reserved_ports中,TCP/IP協議棧從ip_local_port_range中隨機選取源端口時,會排除ip_local_reserved_ports中定義的端口,所以就不會出現端口被佔用了服務沒法啓動。、
推薦使用第二種方法。
$ cat /proc/sys/net/ipv4/ip_local_port_range
32000 61000
$ cat /proc/sys/net/ipv4/ip_local_reserved_ports
8080,9148
因爲本人經驗有限,文章中不免會有錯誤,請瀏覽文章的您指正或有不一樣的觀點共同探討!