老J做IT這塊有二十多個年頭了,算是中國IT的見證者與參與者。
那個時候剛開始接觸和了解時,對於他的一些建議,我不是很樂於去接受,因為我覺得他的那一套技術體系不是很適合如今的情況,當時間久了後發現,他對於資料庫的設計、Java底層等有很多獨到的見解和豐富的實踐經驗等,還是很值得學習和借鑑的。以下是老J的技術分享(其中也有我自己的一些分享進行補充完善),由於時間比較長,我只能記的個大概,不過好在當初做了一些筆記。
一、執行緒急需避免的兩類情況
- 死鎖;
- 死迴圈。
1.死鎖
(1)什麼是死鎖?
死鎖是指兩個或兩個以上的程式在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程式成為死鎖程式。
(2)產生死鎖的原因主要包括哪些?
- 系統資源不足;
- 程式執行的順序問題;
- 資源分配不當。
(3)產生死鎖的四個必要條件有哪些?
- 互斥條件:一個資源每次只能被一個程式使用。
- 請求與保持條件:一個程式因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:程式已獲得資源,在未使用完之前,不能強行剝離。
- 迴圈等待條件:若干程式之間形成一種頭尾相接的迴圈等待資源關係。
只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發現死鎖。
(4)如何預防死鎖?
由於互斥條件是非共享裝置所必須的,不僅不能改變,還應該加以保證,因此,主要是破壞產生死鎖的後三個條件:
- 破壞請求與保持條件:對一個程式在請求資源時,它不能持有不可剝奪資源。
- 破壞不可剝奪條件:對一個已經保持某些不可剝奪資源的程式,提出新的資源請求而不能得到滿足時,它必須釋放已經保持的所有資源,待以後需要時再重新申請。
- 破壞迴圈等待條件:對系統所有資源型別進行線性排列,並賦予不同序號。規定每個程式必須按序號遞增的順序請求資源。
(5)避免死鎖的方法有哪些?
- 一次封鎖法:每個程式(事務)將所要使用的資料全部加鎖,否則,就不能繼續執行。
- 順序封鎖法:預先對資料物件規定一個封鎖順序,所有程式(事務)都按這個順序枷鎖。
- 銀行家演算法:保證程式處於安全程式序列。
(6)哪些方法有助於最大限度地降低死鎖的概率?
- 按同一順序訪問物件;
- 避免事務中的使用者互動;
- 保持事務簡短並在一個批處理中;
- 使用低隔離級別。
2.死迴圈
(1)什麼是死迴圈?
在程式設計中,一個靠自身控制無法終止的程式稱為“死迴圈”。
(2)死迴圈的危害有哪些?
- 程式進入假死狀態(當某個請求導致的死迴圈,該請求將會在很大的一段時間內,都無法獲取介面的返回,程式好像進入假死狀態一樣);
- CPU使用率飆升(程式碼出現死迴圈後,由於沒有休眠,一直不斷搶佔cpu資源,導致cpu長時間處於繁忙狀態,必定會使cpu使用率飆升);
- 記憶體使用率飆升(如果程式碼出現死迴圈時,迴圈體內有大量建立物件的邏輯,垃圾回收器無法及時回收,會導致記憶體使用率飆升。同時,如果垃圾回收器頻繁回收物件,也會造成cpu使用率飆升問題);
- StackOverFlowError棧溢位(在一些遞迴呼叫的場景,如果出現無限遞迴,最終會報StackOverflowError棧溢位,導致程式直接掛掉)。
(3)哪些場景會造成死迴圈?
- 一般迴圈遍歷(for、foreach、while);
- Iterator遍歷;
- 類中使用自己的物件;
- 無限遞迴;
- HashMap使用不當;
- 動態代理使用不當。
(4)寫程式碼如何避免死迴圈?
對於Java程式設計師而言,需要嚴謹地對待自己寫的Code(不可太過隨意,程式碼邏輯嚴謹性體現編寫者的思維邏輯),同時要深入地熟悉Java,注重基本功,這樣一來就能避免寫死迴圈程式碼。
二、快取
1.快取是什麼?
快取就是資料交換的緩衝區(稱作Cache),當某一硬體要讀取資料時,會首先從快取中查詢需要的資料,如果找到了則直接執行,找不到的話則從記憶體中找。由於快取的執行速度比記憶體快得多,故快取的作用就是幫助硬體更快地執行。
2.為什麼要使用快取?
快取的本質就是用空間換時間,犧牲資料的實時性,以伺服器記憶體中的資料暫時代替從資料庫讀取最新的資料,減少資料庫IO,減輕伺服器壓力,減少網路延遲,加快頁面開啟速度。
3.在Java中對於快取有哪些現成的解決方案?
- EhCache(Java分散式快取框架);
- Cacheonix(高效能Java分散式快取系統);
- ASimpleCache(輕量級Android快取框架);
- JBoss Cache(基於事務的Java快取框架);
- Voldemort(基於Java開發的分散式鍵-值快取系統,像JBoss Cache一樣,Voldemort同樣支援多臺伺服器之間的快取同步,以增強系統的可靠性和讀取效能)。
其中我僅僅只使用過Ehcache,關於Ehcache的使用,感興趣的朋友可以參考我的這篇部落格:
SSM框架之整合EhCache
4.Ehcache和Redis的區別又什麼呢?
- 概念不同(Redis是一個基於記憶體的高效能key-value資料庫,而EhCache是純Java程式內快取框架);
- 執行程式不同(Redis獨立執行程式需單獨安裝與Java程式間接關聯(如SpringBoot需要操作Redis等),而Ehcache與Java程式直接繫結在一起,Java程式掛掉,Ehcache也會隨之掛掉,而Redis卻能保持完好);
- 場景不同(Redis適合分散式快取,而EhCahce僅適合單個應用快取)。
5.現成解決方案的利與弊有哪些?分情況來看。
(1)EhCache
a.EhCache的優點有哪些?
- 快速;
- 簡單;
- 快取資料有兩級:記憶體和磁碟,因此無需擔心容量問題;
- 快取資料會在虛擬機器重啟的過程中寫入磁碟;
- 可以通過RMI、可插入API等方式進行分散式快取;
- 具有快取和快取管理器的偵聽介面;
- 支援多快取管理器例項,以及一個例項的多個快取區域;
- 提供Hibernate的快取實現;
- 多種快取策略,Ehcache提供了對大資料的記憶體和硬碟的儲存,最近版本允許多例項、儲存物件高靈活性、提供LRU、LFU、FIFO淘汰演算法,基礎屬性支援熱配置、支援的外掛多。
b.EhCache的缺點有哪些?
- 使用磁碟Cache的時候非常佔用磁碟空間:這是因為DiskCache的演算法簡單,該演算法簡單也導致Cache的效率非常高。它只是對元素直接追加儲存。因此搜尋元素的時候非常的快。如果使用DiskCache的,在很頻繁的應用中,很快磁碟會滿。
- 不能保證資料的安全:當突然kill掉java程式的時候,可能會產生衝突,EhCache的解決方法是如果檔案衝突了,則重建cache。這對於Cache資料需要儲存的時候可能不利。當然,Cache只是簡單的加速,而不能保證資料的安全。如果想保證資料的儲存安全,可以使用Bekeley DB Java Edition版本。這是個嵌入式資料庫。可以確儲存儲安全和空間的利用率。
(2)Cacheonix
a.Cacheonix的優點有哪些?
- 可靠的分散式Java 快取;
- 通過複製實現高可用性;
- 支援泛型的快取API;
- 可與ORM框架整合;
- 使用資料分割槽實現負載均衡;
- 支援非多播網路;
- 高效能運算;
- 快速的本地Java快取;
- 分散式鎖機制。
b.Cacheonix的缺點有哪些?
我覺得最大的缺點是網上相關的資源太少,哪怕我用Google搜尋也如此,同時該專案start或fork實在是太少,間接說明應用的不是很多。
這裡就講上面兩個,另外三個就不講了,意義不大。
6.快取的優缺點有哪些?
前面提到過快取的優點,如減少資料庫IO、減輕伺服器壓力、減少網路延遲、加快頁面響應速度等。
那麼缺點有哪些呢?凡是有利也有弊,如下:
- 存放記憶體的資料可能丟失(記憶體一斷電就會清空資料);
- 快取的資料可能與資料庫中的不一致;
- 記憶體的成本高;
- 記憶體容量相對硬碟而言要少。
7.Java中常用快取機制有哪些?
- 內部(如HashMap以及前面提到的快取框架等);
- 外部(如Redis、Memcache等)。
8.快取實際的應用場景分為哪幾類?
- CPU快取(是位於CPU與記憶體之間的臨時儲存器,它的容量比記憶體小的多但是交換速度卻比記憶體要快得多);
- 瀏覽器快取(瀏覽器可以快取一些靜態資源,比如圖片、js、css等,這些都是不常變化的內容,所以沒有必要每次都去請求);
- CDN快取(客戶端會先檢查瀏覽器的快取,若快取過期則會像CDN傳送請求,CDN檢查快取資料還未過期,那麼直接返回響應,只需兩步搞定。但是,CDN快取過期,那麼需要嚮應用伺服器發起請求,獲得新的資料響應,這部分新的資料按一定的快取策略會選擇是否快取在CDN中);
- 資料庫快取(存入資料庫的資料具有持久化特點,所謂持久化就是將物件儲存到可永久儲存的儲存裝置中(通常是磁碟));
- 業務層快取(針對具體的業務做資料快取,如部落格的排行榜、點贊功能、佇列(排隊處理)、分散式鎖或單執行緒機制等)。
三、客戶端與服務端資料互動
1.資料傳輸的格式有哪些?
二進位制、文字、json、xml等。
2.資料傳輸格式的效率比較,哪個效率最高?
二進位制>文字>json>xml(從左往右,效率逐漸降低)。
3.聚焦常用-JSON
JSON對於前後端開發人員來說,是每天都要提到的。
(1)JSON為什麼這麼流行?應用場景有哪些?
a.JSON為什麼這麼流行?如下原因:
- 簡潔、簡單、體積小(相對XML而言);
- 上手容易,高效;
- 跨語言(與具體的程式語言無關)。
b.應用場景有哪些?這裡只列舉主要的,即介面返回資料:
- Ajax非同步互動;
- RPC遠端呼叫;
- 前後端分離-後端響應資料;
- API開放;
- 企業間的資料合作。
(2)Java中的JSON解析庫有哪些?
- Json-Simple;
- Gson;
- FastJson;
- Jackson。
(3)從適用場景出發,我們應該如何使用合適的JSON解析庫?
首先選擇一個合適的JSON庫要從多個方面考慮,例如:
- 字串解析成JSON效能;
- 字串解析成JavaBean效能;
- JavaBean構造JSON效能;
- 集合構造JSON效能;
- 易用性。
網上關於Json-lib、Gson、FastJson、Jackson的效率比較有很多,這裡不再贅述。
a.為什麼要使用Gson,它有哪些優點?
- gson支援更深層次關聯查詢;
- gson比json更簡單;
- 非同步傳值非常方便;
- 頁面上eval之後可以直接用 xx.xx 的方式取值;
- 減少格式錯誤導致的程式異常。
b.為什麼使用FastJson,它有哪些優點?
- 速度快;
- 使用廣泛;
- 測試完備;
- 使用簡單;
- 功能完備。
b.為什麼使用Jackson,它有哪些優點?
- 解析大檔案的速度比較快;
- 執行時佔用的記憶體比較少,效能更佳;
- API很靈活,容易進行擴充套件和定製。
d.某位支付寶技術專家這樣概括Json-Simple、Gson、Jackson的適用場景?
- 如果你的應用經常會處理大的JSON檔案,那麼Jackson應該是你的菜。GSON在大檔案上表現得相當吃力。
- 如果你主要是處理小檔案請求,比如某個微服務或者分散式架構的初始化,那麼GSON當是首選。Jackson在小檔案上的表現則不如人意。
- 如果這兩種檔案你都經常會處理到,那麼JSON.Simple對此類場景則更為適合。在不同的檔案大小上Jackson和GSON的表現都不太好。
四、框架
1.框架是什麼?
這裡引用維基百科的概述:
軟體框架,通常指的是為了實現某個業界標準或完成特定基本任務的軟體元件規範,也指為了實現某個軟體元件規範時,提供規範所要求之基礎功能的軟體產品。
框架的功能類似於基礎設施,與具體的軟體應用無關,但是提供並實現最為基礎的軟體架構和體系。軟體開發者通常依據特定的框架實現更為複雜的商業運用和業務邏輯。這樣的軟體應用可以在支援同一種框架的軟體系統中執行。
簡而言之,框架就是制定一套規範或者規則(思想),大家(程式設計師)在該規範或者規則(思想)下工作。或者說使用別人搭好的舞臺來做編劇和表演
2.為什麼要使用框架(從軟體層面出發)?
用前創業公司經理經常說的一句話來概括,不要重複造輪子。
重複造輪子意味著花大量的時間做一些事情(而這件事情已經有了現成的解決方案),效率低,極度消耗時間。
反之,”不重複造輪子”意味著不必從頭再來,而是站在巨人的肩上,這樣一來,效率高,耗時少。這裡從中也體現了框架一個重要特點:重用性。曾經二次開發的電商系統、線上教育、OJ測評、VsCode就是一個很有力的證明。
3.使用框架能帶來哪些好處(從軟體層面出發)?
- 重用程式碼,提高開發效率,節約時間;
- 簡單易上手(例如SpringBoot簡化了過去SSM或SSH框架一堆配置檔案);
- 程式安全性高(前人已經考慮到了,無需再從頭過一遍,當然了這不是絕對,畢竟技術不斷更新迭代,黑客攻擊的手段也亦如此);
- 統一的套路(框架在一定程度上統一了編碼規範,降低了維護成本)。
4.使用框架所帶來的弊端?
- 約束性(受到框架本身的制約);
- 泯然眾人矣(大家都會,一定程度上造就了軟體開發流水線,降低了門檻,加劇了內卷);
- 只會用而不懂工作原理(普遍性,面試的時候才會看,平時大多很少看);
- 相容性(例如A框架和B框架存在衝突)。
5.這是一道思考題,工作三年或五年以上的人和剛工作的童鞋區別到底在哪?僅僅是工作經驗上的積累嗎?
老J在此提出,關鍵在於框架。框架包含的種類有很多?例如思維框架(不侷限於當前)、工作框架(提高個人的工作效率或團隊工作效率等)、為人處事框架(如何與人更好的打交道以及面對生活中的突發或非突發事情又是如何處理的等)。框架不僅僅是經驗上的積累,更是從經驗上提煉的精華,這種精華可以叫原則。
6.我對自己在研發上面的框架進行簡要概率,有這麼幾個?
- 技術選型框架(如何做技術選型等);
- 技術實踐框架(如何提高開發效率、如何有效地排查問題和解決問題、如何更快的學習新技術或深入理解現有技術等);
- 文件框架(如何寫好文件);
- 設計框架(如何做好資料庫的設計);
- 運維框架(從單體到分散式微服務的運維體系構建與實踐等);
- 業務知識框架(所待的IT行業涉及的業務知識積累,如何更快的熟悉業務等)。
五、四大注意事項思考
老J提出四大注意事項:
- 資源消耗(檔案、DB、IO、網路等)不能放入for迴圈。
- 能並行不要序列。
- 能觸發不做結束。
- 事件監聽要統一。
1.資源消耗(檔案、DB、網路等)不能放入for迴圈
關於資源消耗檔案如檔案的輸入輸出、DB的增刪改查、網路通訊等相關程式碼,最好不要放入for迴圈中。以我工作中的觀察與實踐,看到的例子如下(僅僅列舉幾個,不做過多的列舉):
- (1)服務之間的通訊,例如A服務調B服務,而B服務裡有個for迴圈需要根據A服務的傳參來反覆呼叫C服務,C服務不斷的訪問資料庫拿到所需資料,會造成CPU飆升,如果同時多人訪問,可能會造成系統假死(響應慢或遲遲得不到響應並陷入卡頓狀態);
- (2)檔案的讀寫,例如在for迴圈中不斷建立檔案並寫入內容,直接會導致物理硬碟滿了,硬碟一旦滿了,直接會影響伺服器上的所有微服務,導致所有微服務假死,雖然實際並未掛掉,但已停止對外服務,因為服務之間的通訊也是需要消耗磁碟的,關於這個例子,可以閱讀我寫的這篇文章:Linux裝置上沒有空間之覆盤;
- (3)DB的CRUD,例如前段時間公司一同事將程式碼從6到8個小時優化到幾分鐘,之所以變快了是因為將for迴圈程式碼進行優化,不在for迴圈中做DB的操作(關於這個例子,可以閱讀我寫的這篇文章:如何寫好對外的API微服務)。
2.能並行不要序列
並行和序列都是通訊中的資料傳輸方式。
(1)什麼是並行?
並行就是同事執行,就好比單個視窗有3個人要辦事,只需要到空視窗即可立即辦事。在計算機中,同一時刻,有多條指令,在多個CPU上執行,就是並行。
(2)什麼是序列?
序列就是按順序執行,好比銀行只有1個視窗,有3個人要辦事,那麼必須排隊,只有前面的人辦完走人才可以繼續下一個。
在計算機中,同一時刻,只能有一條指令,在一個CPU上執行,後面的指令必須等到前面指令執行完畢才能執行
通過解釋並行和序列的概念,大家就能很容易得出一個結論,並行的效率高於序列。
3.能觸發不做結束
這句話我不是很懂。但觸發對於前端開發而言非常熟悉,例如滑鼠點選事件、鍵盤點選事件等。從某個角度上看,由使用者自己決定觸發什麼樣的事件獲取什麼樣的展示資訊,一定程度上降低資源不必要的消耗。
4.事件監聽要統一
(1)什麼是事件監聽?
事件監聽可以理解為是一種觀察者模式,有資料釋出者(事件源)和資料接受者(監聽器)。
(2)為什麼要事件監聽?
以Java檔案監聽器為例(檔案監聽也是事件監聽的一種),檔案監聽器監聽檔案目錄的生成或銷燬。
假如我們不用檔案監聽器的話,可能我們就得寫一個程式每時每刻監聽著某個檔案目錄的生成或銷燬,那樣的話,太消耗程式所執行環境的CPU和記憶體了。
(3)事件監聽的應用場景有哪些?
- 專案的初始化;
- 網站的線上人數統計;
- 檔案狀態的監聽;
- 網站的使用者行為;
- 網站的訪問量。
(4)事件監聽為何要統一?
統一是為了便於管理,事件監聽如果不統一,會影響系統的穩定性。
六、全域性思維
1.什麼是全域性思維?
全域性思維是一切從系統整體及其全過程出發的思想和準則,從客觀整體的利益出發,站在全域性的角度看問題、想辦法,做出決策。
2.為什麼需要全域性思維?
- 從不同的角度思考問題,能更接近問題的本質,找到規律和解決辦法;
- 追求長遠利益,避免蠅頭小利矇蔽雙眼。
3.如何培養全域性思維(某位管理顧問提供瞭如下方法)?
- 用心想(每天睡覺前花個十分鐘,用心想一下:我今天做的事與全域性性有什麼關係,產生了什麼價值,有什麼需要改進的地方。天長日久,全域性性思維就慢慢建立起來了);
- 事上練(做事的時候,心中牢記目標,偏離方向了立即調整);
- 多交流(和有全域性思維的人多交流(不僅僅是有全域性思維的人,與不同領域或某一項精深的人多多交流),聽聽他的反饋。用心想一想,我應該如何改進)。
3.程式設計師思維
(1)什麼是程式設計師思維?
在理性思維的框架下,利用相應工具,來解決相應實際的問題。
例如有位知乎朋友這樣概括程式設計師思維:
職業化的程式設計師會養成一種思維定式,在做任何事之前會去思考:“能不能將這件事中需要重複執行的部分抽象出來?能不能不用人工參與就可以完成?”
(2)程式設計師思維的優缺點有哪些?
a.優點
- 理性(基於邏輯思考問題,對就是對,錯就是錯,不摻雜主觀);
- 實用主義(不管黑貓白貓,能抓住耗子就是好貓);
- 做事情更注重步驟、效率、可行性。
b.缺點
- 過去追求完美,死扣細節,鑽牛角;
- 過於理性;
- 過於實用主義;
- 習慣於機器打交通,不善於與人溝通;
- 習慣單兵作戰,易忽略團隊合作。
(3)如何突破程式設計師思維的定勢?
- 學會透過技術發現問題的本質(技術的目的在於問題的解決,必須要明確這一點);
- 像專家一樣給出意見(參加需求會或其他討論會思考並給出自己的意見);
- 從不同的視角出發(可能要求你你具備該領域不同崗位的基本職能如運維、測試、開發、專案實施、產品經理、UI設計等,不一定要做到精通,一個領域精通很難,更何況多個領域);
- 提高創造力(可以理解為創新,這樣的創造力建立在長年不間斷地編碼實踐、持續地學習借鑑、定期覆盤的條件上);
- 提高全域性思維(前面提到過,這裡不在贅述)。
七、總結
此次老J的技術分享之總結包含六個方面分別為:
- 執行緒急需避免的兩類情況;
- 快取;
- 客戶端與服務端資料互動;
- 框架;
- 四大注意事項思考;
- 全域性思維。
其實技術領域的知識遠遠不止這些,但更重要的是深入其中,並結合過往的實踐經驗進行復盤。
本次分享就寫到這,希望對大家有一定的幫助。