㈠ JDK成長記7:3張圖搞懂HashMap底層原理!
一句話講, HashMap底層數據結構,JDK1.7數組+單向鏈表、JDK1.8數組+單向鏈表+紅黑樹。
在看過了ArrayList、LinkedList的底層源碼後,相信你對閱讀JDK源碼已經輕車熟路了。除了List很多時候你使用最多的還有Map和Set。接下來我將用三張圖和你一起來探索下HashMap的底層核心原理到底有哪些?
首先你應該知道HashMap的核心方法之一就是put。我們帶著如下幾個問題來看下圖:
如上圖所示,put方法調用了putVal方法,之後主要脈絡是:
如何計算hash值?
計算hash值的演算法就在第一步,對key值進行hashCode()後,對hashCode的值進行無符號右移16位和hashCode值進行了異或操作。為什麼這么做呢?其實涉及了很多數學知識,簡單的說就是盡可能讓高16和低16位參與運算,可以減少hash值的沖突。
默認容量和擴容閾值是多少?
如上圖所示,很明顯第二步回調用resize方法,獲取到默認容量為16,這個16在源碼里是1<<4得到的,1左移4位得到的。之後由於默認擴容因子是0.75,所以兩者相乘就是擴容大小閾值16*0.75=12。之後就分配了一個大小為16的Node[]數組,作為Key-Value對存放的數據結構。
最後一問題是,如何進行hash定址的?
hash定址其實就在數組中找一個位置的意思。用的演算法其實也很簡單,就是用數組大小和hash值進行n-1&hash運算,這個操作和對hash取模很類似,只不過這樣效率更高而已。hash定址後,就得到了一個位置,可以把key-value的Node元素放入到之前創建好的Node[]數組中了。
當你了解了上面的三個原理後,你還需要掌握如下幾個問題:
還是老規矩,看如下圖:
當hash值計算一致,比如當hash值都是1100時,Key-Value對的Node節點還有一個next指針,會以單鏈表的形式,將沖突的節點掛在數組同樣位置。這就是數據結構中所提到解決hash 的沖突方法之一:單鏈法。當然還有探測法+rehash法有興趣的人可以回顧《數據結構和演算法》相關書籍。
但是當hash沖突嚴重的時候,單鏈法會造成原理鏈接過長,導致HashMap性能下降,因為鏈表需要逐個遍歷性能很差。所以JDK1.8對hash沖突的演算法進行了優化。當鏈表節點數達到8個的時候,會自動轉換為紅黑樹,自平衡的一種二叉樹,有很多特點,比如區分紅和黑節點等,具體大家可以看小灰演算法圖解。紅黑樹的遍歷效率是O(logn)肯定比單鏈表的O(n)要好很多。
總結一句話就是,hash沖突使用單鏈表法+紅黑樹來解決的。
上面的圖,核心脈絡是四步,源碼具體的就不粘出來了。當put一個之後,map的size達到擴容閾值12,就會觸發rehash。你可以看到如下具體思路:
情況1:如果數組位置只有一個值:使用新的容量進行rehash,即e.hash & (newCap - 1)
情況2:如果數組位置有鏈表,根據 e.hash & oldCap == 0進行判斷,結果為0的使用原位置,否則使用index + oldCap位置,放入元素形成新鏈表,這里不會和情況1新的容量進行rehash與運算了,index + oldCap這樣更省性能。
情況3:如果數組位置有紅黑樹,根據split方法,同樣根據 e.hash & oldCap == 0進行樹節點個數統計,如果個數小於6,將樹的結果恢復為普通Node,否則使用index + oldCap,調整紅黑樹位置,這里不會和新的容量進行rehash與運算了,index + oldCap這樣更省性能。
你有興趣的話,可以分別畫一下這三種情況的圖。這里給大家一個圖,假設都出發了以上三種情況結果如下所示:
上面源碼核心脈絡,3個if主要是校驗了一堆,沒做什麼事情,之後賦值了擴容因子,不傳遞使用默認值0.75,擴容閾值threshold通過tableSizeFor(initialCapacity);進行計算。注意這里只是計算了擴容閾值,沒有初始化數組。代碼如下:
竟然不是大小*擴容因子?
n |= n >>> 1這句話,是在干什麼?n |= n >>> 1等價於n = n | n >>>1; 而|表示位運算中的或,n>>>1表示無符號右移1位。遇到這種情況,之前你應該學到了,如果碰見復雜邏輯和演算法方法就是畫圖或者舉例子。這里你就可以舉個例子:假設現在指定的容量大小是100,n=cap-1=99,那麼計算過程應該如下:
n是int類型,java中一般是4個位元組,32位。所以99的二進制:0000 0000 0000 0000 0000 0000 0110 0011。
最後n+1=128,方法返回,賦值給threshold=128。再次注意這里只是計算了擴容閾值,沒有初始化數組。
為什麼這么做呢?一句話,為了提高hash定址和擴容計算的的效率。
因為無論擴容計算還是定址計算,都是二進制的位運算,效率很快。另外之前你還記得取余(%)操作中如果除數是2的冪次方則等同於與其除數減一的與(&)操作。即 hash%size = hash & (size-1)。這個前提條件是除數是2的冪次方。
你可以再回顧下resize代碼,看看指定了map容量,第一次put會發生什麼。會將擴容閾值threshold,這樣在第一次put的時候就會調用newCap = oldThr;使得創建一個容量為threshold的數組,之後從而會計算新的擴容閾值newThr為newCap*0.75=128*0.75=96。也就是說map到了96個元素就會進行擴容。
除了今天知識,技能的成長,給大家帶來一個金句甜點,結束我今天的分享:堅持的三個秘訣之一目標化。
堅持的秘訣除了上一節提到的視覺化,第二個秘訣就是目標化。顧名思義,就是需要給自己定立一個目標。這里要提到的是你的目標不要定的太高了。就比如你想要增加肌肉,給自己定了一個目標,每天5組,每次10個俯卧撐,你看到自己胖的身形或者海報,很有刺激,結果開始前兩天非常厲害,干勁十足,特別奧利給。但是第三天,你想到要50個俯卧撐,你就不想起床,就算起來,可能也會把自己撅死過去......其實你的目標不要一下子定的太大,要從微習慣開始,比如我媳婦從來沒有做過俯卧撐,就讓她每天從1個開始,不能多,我就怕她收不住,做多了。一開始其實從習慣開始,先變成習慣,再開始慢慢加量。量太大養不成習慣,量小才能養成習慣。很容易做到才能養成,你想想是不是這個道理?
所以,堅持的第二個秘訣就是定一個目標,可以通過小量目標,養成微習慣。比如每天你可以讀五分鍾書或者5分鍾成長記,不要多,我想超過你也會睡著了的.....
最後,大家可以在閱讀完源碼後,在茶餘飯後的時候問問同事或同學,你也可以分享下,講給他聽聽。
㈡ 一文解密Kafka,Kafka源碼設計與實現原理剖析,真正的通俗易懂
Apache Kafka (簡稱Kafka )最早是由Linkedln開源出來的分布式消息系統,現在是Apache旗下的一個子項目,並且已經成為開冊、領域應用最廣泛的消息系統之 Kafka社區也非常活躍,從 版本開始, Kafka 的標語已經從「一個高吞吐量、分布式的消息系統」改為「一個分布式的流平台」
關於Kafka,我打算從入門開始講起,一直到它的底層實現邏輯個原理以及源碼,建議大家花點耐心,從頭開始看,相信會對你有所收獲。
作為 個流式數據平台,最重要的是要具備下面 個特點
消息系統:
消息系統 也叫作消息隊列)主要有兩種消息模型:隊列和發布訂Kafka使用消費組( consumer group )統 上面兩種消息模型 Kafka使用隊列模型時,它可以將處理 作為平均分配給消費組中的消費者成員
下面我們會從 個角度分析Kafka 的幾個基本概念,並嘗試解決下面 個問題
消息由生產者發布到 fk 集群後,會被消費者消費 消息的消費模型有兩種:推送模型( pu和拉取模型( pull 基於推送模型的消息系統,由消息代理記錄消費者的消費狀態 消息代理在將消息推送到消費者後 標記這條消息為已消費
但這種方式無法很好地保證消息的處理語義 比如,消息代理把消息發送出去後,當消費進程掛掉或者由於網路原因沒有收到這條消息時,就有可能造成消息丟失(因為消息代理已經 這條消息標記為自己消費了,但實際上這條消息並沒有被實際處理) 如果要保證消息的處理語義,消息代理發送完消息後,要設置狀態為「已發送」,只有收到消費者的確認請求後才更新為「已消費」,這就需要在消息代理中記錄所有消息的消費狀態,這種做法也是不可取的
Kafka每個主題的多個分區日誌分布式地存儲在Kafka集群上,同時為了故障容錯,每個分區都會以副本的方式復制到多個消息代理節點上 其中一個節點會作為主副本( Leader ),其 節點作為備份副本( Follower ,也叫作從副本)
主副本會負責所有的客戶端讀寫操作,備份副本僅僅從主副本同步數據 當主副本 IH 現在故障時,備份副本中的 副本會被選擇為新的主副本 因為每個分區的副本中只有主副本接受讀寫,所以每個服務端都會作為某些分區的主副本,以及另外一些分區的備份副本這樣Kafka集群的所有服務端整體上對客戶端是負載均衡的
消息系統通常由生產者「pro ucer 消費者( co sumer )和消息代理( broke 大部分組成,生產者會將消息寫入消息代理,消費者會從消息代理中讀取消息 對於消息代理而言,生產者和消費者都屬於客戶端:生產者和消費者會發送客戶端請求給服務端,服務端的處理分別是存儲消息和獲取消息,最後服務端返回響應結果給客戶端
新的生產者應用程序使用 af aP oce 對象代表 個生產者客戶端進程 生產者要發送消息,並不是直接發送給 務端 ,而是先在客戶端 消息放入隊列 然後 一個 息發送線程從隊列中消息,以 鹽的方式發送消息給服務端 Kafka的記 集器( Reco dACCUl'lUlato )負責緩存生產者客戶端產生的消息,發送線程( Sende )負責讀取 集器的批 過網路發送給服務端為了保證客戶端 絡請求 快速 應, Kafka 用選擇器( Selecto 絡連接 讀寫 理,使網路連接( Netwo kCl i.ent )處理客戶端 絡請求
追加消息到記錄收集器時按照分區進行分組,並放到batches集合中,每個分區的隊列都保存了將發送到這個分區對應節點上的 記錄,客戶端的發送線程可 只使用 Sende 線程迭 batches的每個分區,獲取分區對應的主劇本節點,取出分區對應的 列中的批記錄就可以發送消息了
消息發送線程有兩種消息發送方式 按照分區直接發送 按照分區的目標節點發迭 假設有兩台伺服器, 題有 個分區,那麼每台伺服器就有 個分區 ,消息發送線程迭代batches的每個分 接往分區的主副本節點發送消息,總共會有 個請求 所示,我 先按照分區的主副本節點進行分組, 屬於同 個節點的所有分區放在一起,總共只有兩個請求做法可以大大減少網路的開銷
消息系統由生產者 存儲系統和消費者組成 章分析了生產者發送消息給服務端的過程,本章分析消費者從服務端存儲系統讀取生產者寫入消息的過程 首先我 來了解消費者的 些基礎知識
作為分布式的消息系統, Kafka支持多個生產者和多個消費者,生產者可以將消息發布到集群中不同節點的不同分區上;「肖費者也可以消費集群中多個節點的多個分區上的消息 寫消息時,多個生產者可以 到同 個分區 讀消息時,如果多個消費者同時讀取 個分區,為了保證將日誌文件的不同數據分配給不同的消費者,需要採用加鎖 同步等方式,在分區級別的日誌文件上做些控制
相反,如果約定「同 個分區只可被 個消費者處理」,就不需要加鎖同步了,從而可提升消費者的處理能力 而且這也並不違反消息的處理語義:原先需要多個消費者處理,現在交給一個消費者處理也是可以的 3- 給出了 種最簡單的消息系統部署模式,生產者的數據源多種多樣,它們都統寫人Kafka集群 處理消息時有多個消費者分擔任務 ,這些消費者的處理邏輯都相同, 每個消費者處理的分區都不會重復
因為分區要被重新分配,分區的所有者都會發生變 ,所以在還沒有重新分配分區之前 所有消費者都要停止已有的拉取錢程 同時,分區分配給消費者都會在ZK中記錄所有者信息,所以也要先刪ZK上的節點數據 只有和分區相關的 所有者 拉取線程都釋放了,才可以開始分配分區
如果說在重新分配分區前沒有釋放這些信息,再平衡後就可能造成同 個分區被多個消費者所有的情況 比如分區Pl 原先歸消費者 所有,如果沒有釋放拉取錢程和ZK節點,再平衡後分區Pl 被分配給消費者 了,這樣消費者 和消費者 就共享了分區Pl ,而這顯然不符合 fka 中關於「一個分區只能被分配給 個消費者」的限制條件 執行再平衡操作的步驟如下
如果是協調者節點發生故障,服務端會有自己的故障容錯機制,選出管理消費組所有消費者的新協調者節,點消費者客戶端沒有權利做這個工作,它能做的只是等待一段時間,查詢服務端是否已經選出了新的協調節點如果消費者查到現在已經有管理協調者的協調節點,就會連接這個新協調節,哉由於這個協調節點是服務端新選出來的,所以每個消費者都應該重新連接協調節點
消費者重新加入消費組,在分配到分區的前後,都會對消費者的拉取工作產生影響 消費者發送「加入組請求」之前要停止拉取消息,在收到「加入組響應」中的分區之後要重新開始拉取消息時,為了能夠讓客戶端應用程序感知消費者管理的分區發生變化,在加入組前後,客戶端還可以設置自定義的「消費者再平衡監聽器」,以便對分區的變化做出合適的處理
㈢ 什麼是低代碼開發
低代碼開發平台(Low-Code Development Platform,LCDP)是低代碼開發所需的環境。大多數低代碼平台都是以雲上提供的aPaaS(Application Platform as a Service,應用程序平台即服務)的形式,不僅用於開發,還用於應用程序的運行,實現了軟體開發到應用的一貫性支持。
所謂低代碼開發,是指盡量無需編寫源代碼,通過使用「圖形用戶界面/GUI」這一可視化操作,在極短的時間內實現系統開發的手法。目前也有通過在Web瀏覽器上搜索所需組件,整合粘貼來製作應用程序的工具。
採用低代碼開發,無需SQL記述就可以製作資料庫,簡化開發工序。在保證一定擴展性的同時,可以有效縮短開發工時。
低代碼開發平台最初被關注的是用於移動應用的開發。與基礎系統開發相比,手機app開發對速度的要求更高,而且還必須支持多設備。在傳統意義上,要在短時間內推出這樣的移動應用程序是非常困難的,於是,低代碼開發平台進入了開發人員的視野。
在現今社會,低代碼開發平台受到關注的最大理由是數字化轉型(DX)。所謂數字化轉型,是指通過人工智慧和物聯網等信息技術,將一切事物通過數字數據連接起來,從而從根本上改變企業業務模式。
目前許多企業都在致力於數字化轉型,以求在高速發展的時代中生存下來。企業的IT部門為了推進數字化轉型,必須更密集的進行軟體開發。但是,軟體開發技術人員的數量是遠遠不夠的,僅靠IT部門根本無法滿足軟體開發的需求。
低代碼開發平台,可以被一般的業務人員、一線工作人員、管理人員等非專業的開發人員使用,無論是否有開發基礎或經驗,都可以經過簡單的培訓進行軟體開發。
縮短開發時間
低代碼開發最大的優勢是可以縮短開發時間,也就節約了開發成本。低代碼開發平台提供了大量的通用組件,可以實現一些基礎功能。必要時可以添加自己編寫的代碼,來滿足用戶的功能需求,提供質量穩定的應用程序。
無需擔心安全性
低代碼開發平台的供應商會提供相應的安全對策,用戶無需擔心程序的安全性以及開發過程中的安全風險。為了實現特殊功能,需要自己編寫代碼時,用戶只需關注自己的編寫部分的安全性即可。
降低開發門檻
在低代碼開發中,無需編寫復雜的源代碼,就可以在專用的平台上編寫程序。即使沒有受過編程專業教育的人,也可以在平台上輕松地進行開發工作。在傳統印象中,程序開發都是由專業的工程師來完成,使用低代碼開發平台,程序開發的門檻大大降低了。
通用性組件
低代碼開發平台提供了大量通用的組件,這些可供使用的組件種類多樣。
此外,用戶也可以利用第三方開發的組件。這樣的可再利用形式的組件,支撐著在低代碼開發平台的視覺建模。
視覺建模
低代碼開發平台以模型驅動型開發為基礎,任何人都可以通過可視化建模,輕松實現程序開發。
通過拖放可以將所需的流程和組件整合,無需編程即可創建程序。同時,有編程技能的工程師也可以根據需要進行編碼,從而定製組件。
支持各種架構
要開發與企業架構相對應的基礎系統,就必須具備與各種系統協作的功能。在這一點上,低代碼開發平台支持大多數的主流操作系統和資料庫。
另外,通過豐富種類的API,可以和外部系統自由合作。因此,低代碼開發平台具有可擴展性和開放架構,可支持大企業的基礎系統開發。
基於代碼的擴展
完全不使用代碼的無代碼工具,特點是使用預設好的功能來製作簡單的應用程序。對於低代碼開發平台,用戶也可以通過編碼自由擴展組件的功能。
低代碼開發平台可以根據客戶各自的需求,進行各種各樣的定製,即使是復雜的大規模系統開發也能應對。
軟體全生命周期整體支持
目前提供的低代碼開發平台大部分採用的是雲服務aPaaS的形式。
因此,低代碼開發平台並非單純的應用開發工具,從與資料庫的自動連接、測試、正式啟動、進一步運行管理、變更管理等開發工序到實際運用工序,搭載了支持整個軟體生命周期的功能。
與低代碼開發平台對應的是無代碼開發平台(No-Code Development Platform, NCDP)。無代碼顧名思義,就是不以編寫代碼為前提的開發方法。
低編碼和無編碼在某種程度上非常相似。前述的可視化建模、可再利用的通用性組件、生命周期管理等低代碼開發平台的特徵也適用於無代碼開發平台。
無代碼開發平台同樣適用於專業的開發者和無基礎開發者,提供了能夠在更短的時間內輕松發布應用的環境。無需通過編碼進行編程,通過PaaS平台提供,可以在不構建操作環境的情況下立即運行。
乍一看,無代碼開發平台只是從低代碼開發平台中去掉了編寫代碼的要素。但實際上它們是非常不同的。
首先,無代碼開發平台不能通過編寫代碼來擴展或定製功能。因此,無代碼開發平台目標是通過預先准備好的組件和API,實現功能范圍內的相對簡單的程序開發。不需要專業開發人員進行系統設計和資料庫設計,經過簡單的功能設計後立刻就可以使用。
典型的例子是使用Excel等電子表格的普通業務人員,將Excel和紙質工作系統化,自己創建應用程序,以簡化日常工作。這些應用程序由其使用者來完成運營,隨著業務的變化可以靈活的調整應用程序的功能。
無代碼開發平台不能通過編寫代碼擴展功能,所以不適合功能較多的程序開發。此外,由於API的系統協作自由度較低,也不適合核心系統的開發。
得益於一些廠商的努力,低代碼行業正在構建起健康的生態。我們在討論低代碼的未來時,需要清楚一點的是,低代碼並非萬能,它有清晰的能力邊界,而非一些聲音所說的會「搶走程序員的飯碗」。低代碼是企業數字化建設當中「最後一公里」,在保障企業數字化進程的價值賦能下,中國市場會有低代碼的一方天地。
國內的簡搭(jabdp)開發平台是一個低代碼開發平台,復雜的業務功能,只需要會基本的sql語句和javascript語法,就能進行快速開發,滿足其個性化的業務需求,設計出各種復雜的企業web應用。主要特點如下:
可靈活定製:簡搭(jabdp)低代碼平台提供了強大的定製能力,包括頁面定製、數據表管理、業務流程定製等,便於實現各類企業應用。
許可權管理:簡搭(jabdp)低代碼平台提供組織結構管理和精細的許可權管理多人,便於企業根據實際情況靈活地進行許可權設置和調整,促進內部協作。
易於部署和維護:簡搭(jabdp)低代碼平台提供一鍵部署功能,無需配置復雜的網路伺服器;根據企業的需求變化進行系統維護也更容易。
支持二次開發和系統集成:簡搭(jabdp)低代碼平台是一個開放的快速開發平台,有經驗的程序員依然可以基於jabdp定製開發出許多高級的功能,而不受jabdp本身的限制;同時,簡搭(jabdp)低代碼平台開發出的應用也可以很方便地與企業的現有信息系統集成,或者與微信、釘釘等第三方應用集成。
簡搭(jabdp)低代碼平台適合用於大部分的企業級web應用的開發,尤其適合企業信息管理系統(MIS)、企業資源計劃系統(ERP)、客戶關系管理系統(CRM),業務支撐系統(BSS)等。並且就一些經典的項目案例提取整合出各種類型的項目模板,共享給開發者參考,開發者可以在原有的項目基礎上進行修改定製,以打造其個性化的企業信息化平台。
關於低代碼開發平台,一個常見的誤區是認為「低代碼開發平台只適用於平民開發者(CitizenDeveloper)」。平民開發者是指那些非傳統的、沒有受過專業編程教育的開發者,他們主要用低代碼平台來創造和定製應用。
當然,通過使用低代碼開發平台,可以降低對多數項目人員的技術要求。在多個項目並行時,只需要一個有經驗的程序員進行數據表、業務邏輯等的設計即可,其他項目人員無需編程能力,這樣在人月數和人員要求兩個方向都能有效控制項目的投入。
藉助低代碼開發平台,可以更高效地理解中小企業的信息化項目需求,控制項目開發的成本,同時適應中小企業信息化需求變化快和缺乏專業技術人員進行項目維護的特點,是開發中小企業信息化項目的最佳選擇。