【JVM】垃圾回收器總結(2)——七種垃圾回收器類型_網頁設計公司

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

七種垃圾回收器類型

GC的約定參數

DefNew——Default New Generation

Tenured——Serial Old

ParNew——Parallel New Generation

PSYoungGen——Parallel Scavenge

ParOldGen——Parallel Old Generation

適用範圍:只需要掌握Server模式,Client模式基本不會用。

操作系統:32位windows不論硬件如何默認使用Client模式。32位其他OS,2G內存同時2個CPU以上用Server模式,低於該配置是Client模式。64位只有Server模式。

七大垃圾收集器

串行GC:Serial收集器(1:1)

串行收集器是最古老的,最穩定,效率高的收集器,只使用一個線程去回收但其進行垃圾回收過程中可能會產生較長的停頓。雖然在收集垃圾的過程中需要暫停其他的工作線程,但是簡單高效,對於單CPU環境來說,沒有線程交互的開銷可以獲得最高的單線程垃圾收集效率,因此Serial垃圾回收器依然是Java虛擬機運行在Client模式下默認的新生代垃圾回收器。

開啟串行收集器的JVM參數是-XX:+UseSerialGC。

開啟後會使用:Serial(Young區)+ Serial Old(Old區)的收集器組合。表示新生代、老年代都會使用串行回收收集器,新生代用複製算法,老年代用標記整理算法。

顯式激活垃圾回收器:

并行GC:ParNew(N:1)

使用多線程進行垃圾回收,在垃圾回收時,會暫停所有其他工作線程,直到GC結束。

ParNew時Serial收集器新生代的并行多線程版本,最常見的應用場景是配合老年代CMS GC工作,其餘行為和Seria收集器完全一樣,ParNew垃圾收集器在垃圾收集過程中同樣要暫停所有其他的工作線程。它是很多JVM運行在Server模式下新生代的默認垃圾收集器。

開啟串行收集器的JVM參數是-XX:+UseParNewGC。

啟用ParNew收集器,隻影響新生代的收集(新生代GC頻繁),不影響老年代。開啟參數后,會使用ParNew(Young區)+Serial(Old區)的收集器組合。新生代使用複製算法,老年代使用標記整理算法。

ParNew+Tenured(Serial Old)不再推薦使用:

并行回收:Parallel Scavenge(N:N)

Parallel Scavenge收集器類似ParNew,也是一個新生代垃圾收集器,使用複製算法,也是一個并行的多線程的垃圾收集器,俗稱吞吐量優先收集器。相當於是串行收集器在新生代和老年代的并行化。

它重點關注可控制吞吐量,高吞吐量意味着高效利用CPU時間,它多用於在後台運算而不需要太多交互的任務。【吞吐量  = 用戶代碼運行時間/(用戶代碼運行時間+垃圾回收時間)】

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

自適應調節策略也是Parallel Scavenge收集器與ParNew收集器的一個重要區別。自適應調節策略就是JVM會根據當前系統的運行情況看收集性能監控信息,動態調整這些參數以提供最合適的停頓時間(-XX: MaxGCPauseMills)或最大吞吐量。

常用的JVM參數:-XX:+UseParallelGC 或者 +UseParallelOldGC(二者可以互相激活),使用Parallel Scavenge收集器。開啟參數后,新生代用複製算法,老年代用複製標記整理算法。

參數-XX:+ParallelGCThread = K 表示啟動K個GC線程【CPU > 8  K = 5或8 CPU < 8 K = 實際個數】

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程的標記整理算法,在JDK1.6開始提供。

JDK1.6之前,新生代使用Parallel Scavenge收集器只能搭配老年代Serial Old收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量。在JDK1.6之前,是Parallel Scavenge+Serial Old。

Parallel Old是為了老年代同樣提供吞吐量優先的垃圾收集器,如果系統對吞吐量要求較高,JDK1.8后優先考慮新生代Parallel Scavenge和老年代 Parallel Old的搭配策列。在JDK1.8及之後,是Parallel Scavenge+Parallel Old。

JVM常用參數:-XX:+UseParallelOldGC使用Parallel Old收集器。和上一個part的截圖是一致的,UseParallelGC和UseParallelOldGC可以互相激活。

併發標記清除GC(CMS)

CMS收集器是一個以獲取最短回收停頓時間為目標的收集器。適合應用在互聯網站或BS系統的服務器上,因為這類場景重視服務器的響應速度,希望系統的停頓時間盡可能短。CMS適合堆內存大、CPU核數多的服務器端應用,也是G1出現之前大型應用的首選收集器。

CMS的優勢是併發收集停頓少,併發是指與用戶線程一起執行。

開啟收集器的JVM參數:-XX:+UseConcMarkSweepGC 開啟後會自動開啟 -XX:+UseParNewGC

併發標記清除收集器的組合:ParNew + CMS + Serial Old(作為CMS出錯的後備收集器,增強健壯性)

CMS內存回收一共有4個過程

  1. 初始標記:只有標記一下GC Roots能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程。
  2. 併發標記:進行GC Roots跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程,主要標記過程,標記全部對象。
  3. 重新標記:修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,仍然需要暫停所有的工作線程。由於併發標記時,用戶線程依然運行,因此在正式清理前再做修正。
  4. 併發清除:清除GC Roots不可達對象,和用戶線程一起工作,不需要暫停工作線程。基於標記結果,直接清理對象。由於耗時最長的併發標記和併發清除過程中,垃圾收集線程可以和用戶線程一起併發工作。所以總體上說CMS收集器的內存回收和用戶線程是併發執行的(初始標記和重新標記雖然要暫停,但是用時很短)。

優點:併發收集,停頓次數少。

缺點:對CPU的壓力大,CMS在收集和應用線程會同時增加對堆內存的佔用,也就是i說CMS必須在老年代堆內存用完之前完成GC,否則CMS會回收失敗,將觸發擔保機制,Serial Old會以STW(Stop The World,暫停所有工作線程)的方式進行依次GC,從而造成較大的停頓時間。而且採用標記清除算法會產生內存碎片。

Serial Old收集器

Serial Old收集器是Serial垃圾收集器老年代版本,同樣是單線程的收集器,使用標記整理算法。

主要運行在Client默認的JVM老年代垃圾回收器。

在Server模式下,主要有兩個用途:

  1. 在JDK1.5之前與新生代Parallel Scavenge收集器搭配使用。(Parallel Scavenge+Serial Old)
  2. 作為老年代版中使用CMS收集器的後備垃圾回收方案。

G1垃圾回收器

內容較多,見【JVM】垃圾回收器總結(3)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

Web 三維組態的仿真運用案例:民航飛機的數據監控_網頁設計公司

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

前言 在飛機航行的過程中,客艙里座位上方的熒屏上,除了播放電視劇和廣告之外,還會時不時的切換到一個飛機航行的監控系統。這個監控系統的主要目的是,讓乘客可以了解到飛機在航行過程中的整體狀況、距離目的地的航線進程以及一些有可能出現的突發事件。飛機航行的監控系統在一定程度上,可以使乘客在旅途過程中得到準確的信息源,最重要的是可以使乘客在旅途中得到安心。在保障乘客獲取到實際信息源的同時,飛機航行的監控系統也能將採集到的數據及時的反饋給航務人員,而且將數據記錄保存下來,在飛機過站或航后供航務和機務人員使用和監測維護。介於 2D 組態和 3D 組態上,
Hightopo(以下簡稱 HT )的 
HT for Web 產品上的有着豐富的組態化可供選擇,本文將介紹如何運用 HT 豐富的 
2/3D 組態搭建出一個飛機航行的監控系統的解決方案。 預覽地址: flight-monitor  
系統分析 飛機航行監控是一種測量、跟蹤飛機性能變化趨勢的簡單而實用方法。在日常航班飛行中,用機載儀錶記錄飛機巡航時的有關參數,並將其與原有性能進行比較。監控的主要參數有:飛行單位時間消耗的燃油量、飛機的巡航飛行速度和消耗單位燃油量飛機飛過的水平距離。它們分別表示發動機、飛機及飛機發動機的綜合性能。它們分別表示發動機、飛機及飛機發動機的綜合性能。性能監控的結果,既可為飛機維修提供可能的故障信息,又為制定飛行計劃提供可靠的性能依據。 目前,先進的噴氣運輸機都已安裝飛行狀態監控系統,它記錄下有關參數值,並存儲在數據管理裝置中,這些數據可以發送到或在飛機着陸後送到地面站,用專用計算機程序進行計算和修正,使性能監控數據採集、發送、計箅分析工作自動化,節省了人力,提高了精確度。 1、
飛行數據記錄器:自從飛行數據記錄器 FDR(Flight Data Recorder)“俗稱黑匣子”誕生以來,其提供的詳實準確的飛行數據為事故調查、機務維護保障、安全運營監控提供了重要依據,成為航空安全運營的重要管理手段。 2、
ACMS(Aireraft Condition Monitoring System)的系統概述:ACMS 系統是飛機上安裝的先進機載數據採集和處理系統,它能以實時方式收集數據,對發動機狀態和飛行性能進行監控以及進行特殊的工程調查。由飛機通信尋址與報告系統(ACARS)通過地空數據鏈發送到地面接收站,最後傳送到航空公司的終端,也可通過快速存取記錄器(QAR)將數據記錄保存下來,在飛機過站或航后供航務和機務人員使用。 而 HT 實現的業務功能可以通過ACMS 系統採集到的數據,對接到显示終端去展示,通過實時監測的數據準確地显示出來,起到對飛機狀態的監控功能並即時反饋信息數據的變化。  
實現過程
雲中穿行效果 為了達到飛機雲中穿行的效果,最開始我遇到的問題是飛機飛行的層次感,也就通常所說的透視效果,這裏我採用的是雲通道和雲背景以不同的速度流動,製造一種飛行的透視效果。   雲我採用的是貼圖的方式呈現的,但是僅僅是貼圖會遮擋天空和飛機,非常影響飛機飛行的觀感,所以我開啟了相應圖元的 
transparent 和 
opacity,雲背景和雲通道設置不同的透明度,不僅增加了層次感,還會讓人產生雲朵從眼前飄過的錯覺。   雲通道採用的是 
ht.Polyline 類型,通道縮放拉大了 Y 軸的比例,使雲通道有更大的縱向空間,設置 
reverse.flip 背拷貝使雲通道內部也显示出貼圖,彷彿讓飛機置身於雲海中穿梭;雲背景採用 
ht.Node 類型,只設置一個面显示充當雲背景。   整體的雲流動效果採用 
offset 偏移實現,改變相應圖元或相應圖元面的貼圖偏移量來達到飛機雲中穿行的效果, 代碼如下:

var i = 1, p = 0; setInterval(() => { i -= 0.1; p += 0.005; clouds.s('shape3d.uv.offset', [i, 0]); cloudBackground.s('all.uv.offset', [p, 0]); }, 100);

 

升降顛簸效果 雖然達到了飛機雲中穿行的效果,但是如果飛機只是直直的飛行,那也會降低飛行的實感,相信坐過飛機的朋友肯定都遇到過因氣流產生的顛簸,也經常感受到飛機飛行途中的爬升和下降,這其實是因為飛機的航線並不是一直固定在一個高度上,有時會爬升有時會下降,所以我就用 
ht-animation.js HT 動畫擴展插件去實現飛機顛簸效果,代碼如下:

dm.enableAnimation(20);
plane.setAnimation({
    back1: {
        from: 0, to: 160, easing: 'Cubic.easeInOut', duration: 8000, next: "up1", onUpdate: function (value) { value = parseInt(value); var p3 = this.p3(); this.p3(value, p3[1], p3[2]); } }, //...省略相似 start: ["back1"] });

 

球扇形視角限制 飛行效果完善之後,這時我就遇到了一個比較棘手的問題,因為實際上雖然看着飛機是在雲海中穿梭,但是僅僅是在通道中飛行,背景其實也只是平面貼圖,所以當視角到達某種程度的時候就會有強烈的違和感和不真實感,就需要一個視角限制,使視角的調整剛剛好在一個範圍內。   視角限制的話一般是限制 g3d 的 
eye 和 
center,不太了解的朋友可以去看 hightopo 官網中的 3d 手冊,裏面有詳細的說明,這裏我就不再贅述了;因為視角範圍的關係,所以我決定固定 
center 的位置,代碼如下:

g3d.addPropertyChangeListener(e => {
    // 固定中心點 if (e.property === 'center') { e.newValue[0] = center[0]; e.newValue[1] = center[1]; e.newValue[2] = center[2]; } }

然後再把 eye 限制在某一個範圍內就大功告成了,然而這裏卻並不是那麼簡單,最開始我把 eye 限制在一個立方體的空間內,但交互效果很不理想,考慮到 g3d 默認交互中,鼠標拖拽平移視角變換時,實際上 eye 是在一個以 center 為球心的球面上運動的,所以我決定從這個球中挖出來一塊作為 eye 的限制空間,也就是球扇形,不太理解的朋友可以參考這個圖:

 

球扇形視角限制,一共需要三個參數,分別是中心參考軸、中心軸和外邊所成角度、所在球限制半徑,其中中心參考軸可根據初始 eye 和 center 的連接延長線確定,所在球限制半徑又分最大限制和最小限制,代碼如下:

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

function limitEye(g3d, eye, center, options) { var limitMaxL = options.limitMaxL, limitMinL = options.limitMinL, limitA = options.limitA; g3d.addPropertyChangeListener(e => { // 固定中心點 if (e.property === 'center') { e.newValue[0] = center[0]; e.newValue[1] = center[1]; e.newValue[2] = center[2]; } // 限制視角 if (e.property === 'eye') { var newEyeV = new ht.Math.Vector3(e.newValue), centerV = new ht.Math.Vector3(center), refEyeV = new ht.Math.Vector3(eye), refVector = refEyeV.clone().sub(centerV), newVector = newEyeV.clone().sub(centerV); if (centerV.distanceTo(newEyeV) > limitMaxL) { newVector.setLength(limitMaxL); e.newValue[0] = newVector.x; e.newValue[1] = newVector.y; e.newValue[2] = newVector.z; } if (centerV.distanceTo(newEyeV) < limitMinL) { newVector.setLength(limitMinL); e.newValue[0] = newVector.x; e.newValue[1] = newVector.y; e.newValue[2] = newVector.z; } if (newVector.angleTo(refVector) > limitA) { var oldLength = newVector.length(), oldAngle = newVector.angleTo(refVector), refLength = oldLength * Math.cos(oldAngle), vertVector, realVector, realEye; refVector.setLength(refLength); newEyeV = newVector.clone().add(centerV); refEyeV = refVector.clone().add(centerV); vertVector = newEyeV.clone().sub(refEyeV); vertLength = refLength * Math.tan(limitA); vertVector.setLength(vertLength); realVector = vertVector.clone().add(refEyeV).sub(centerV); realVector.setLength(oldLength); realEye = realVector.clone().add(centerV); // 防止移動角度大於 180 度,視角反轉 if (oldAngle > Math.PI / 2) { realEye.negate(); } e.newValue[0] = realEye.x; e.newValue[1] = realEye.y; e.newValue[2] = realEye.z; } } }) }

 

飛機監控系統 當然作為監控系統,自然要有監控了,增加右下角的小地圖,並提供三種模式,分別是聚焦飛機,聚焦飛行軌跡和聚焦地圖,並根據飛機的飛行方向控制飛行軌跡的流動效果,其中聚焦飛機會跟隨飛機移動進行 
fitData,使飛機一直處於小地圖的中心,代碼如下:

var fitFlowP = function (e) { if (e.property === 'position' && e.data === plane) { mapGV.fitData(plane, false); } }; buttonP.s({ 'interactive': true, 'onClick': function (event, data, view, point, width, height) { map.a('fitDataTag', 'plane2D'); mapGV.fitData(plane, false); mapDM.md(fitFlowP); } }); buttonL.s({ 'interactive': true, 'onClick': function (event, data, view, point, width, height) { mapDM.umd(fitFlowP); map.a('fitDataTag', 'flyLine'); mapGV.fitData(flyLine, false); } }); // ...省略

增加鼠標移到飛機相應位置進行名稱的提示、雙擊后显示飛機相應位置的信息面板並將視角聚焦到面板上、點擊飛機任意地方切換回飛機飛行模式等效果。 左側增加監控面板替代上面提到的雙擊相應位置這步操作直接聚焦到相應位置的信息面板上,這裏按鈕開啟了交互並添加了相應的交互邏輯,代碼如下:

button_JC.s({
    'interactive': true, 'onClick': function (event, data, view, point, width, height) { event.preventDefault(); let g3d = G.g3d, g3dDM = G.g3d.dm(); g3d.fireInteractorEvent({ kind: 'doubleClickData', data: g3dDM.getDataByTag(data.getTag()) }) } }); //...省略

 

天空渲染效果 既然是監控系統肯定是 24 小時無差別的監控,這就涉及到一個問題,我總不可能半夜的時候飛機也從瓦藍瓦藍的天空上飛過,這就很欠缺真實性了,所以要有一個天空從亮到暗再從暗到亮的過程,這個過程我暫定到 06:00-06:30 和19:00-19:30 這兩個時間段。   天空採用的是 
shape3d : ‘sphere’ 球形,包裹整個場景,然後使用 
reverse.flip 背拷貝 和 
blend 染色,之後天空就可以渲染成我想要的顏色,如果按照時間改變天空明暗只要改變染色值就可以了。   但是由於白天和晚上光照情況的不同,雲反射光的強度也不同,就導致了白天和晚上雲的差異,所以也要調整雲道和雲背景的貼圖的 
opacity 透明度,晚間更為透明度,代碼如下:

if ((hour > 6 && hour < 19) || (hour == 6 && minutes >= 30)) { timePane && timePane.a({ 'morning.visible': false, 'day.visible': true, 'dusk.visible': false, 'night.visible': false, 'day.opacity': 1 }) skyBox.s({ "shape3d.blend": 'rgb(127, 200, 240)', }) cloudBackground.s({ "back.opacity": 0.7, }) clouds.s({ "shape3d.opacity": 0.7, }) } else if ((hour < 6 || hour > 19) || (hour == 19 && minutes >= 30)) { //...省略 } else if (hour == 6 && minutes < 15 ) { //...省略 } else if (hour == 6 && minutes >= 15 && minutes < 30) { //...省略 } else if (hour == 19 && minutes < 15) { //...省略 } else if (hour == 19 && minutes >= 15 && minutes < 30) { //...省略 }

 

這裏我還增加了對右上角時間面板時間狀態圖標的支持,並增加了圖標切換時的漸隱漸顯效果,同時給時間面板狀態圖標位置增加了點擊切換到下一時間狀態的功能。   為了演示效果我增加了時間倍速按鈕,下圖是 500 倍時間流速下的變化情況:  
總結 身處大數據時代的我們,在網絡科技發展越來越快的環境下,迎來了 5G 的新征程和工業4.0的新變革,在信息數據採集傳輸和數據可視化系統的融合下,可以發掘出許多行業管控可視化系統的解決方案。而 HT 長久以來一直致力於多樣化行業系統可視化的搭建,涉獵的技術行業所累積的經驗,通過自主創新研發的技術產品,已經打造出許多行業上具有代表性意義的可視化管控系統,例如同樣有關於飛機的監控可視化系統: 無人機 3D 可視化系統 2019 我們也更新了數百個工業互聯網 2D/3D 可視化案例集,在這裏你能發現許多新奇的實例,也能發掘出不一樣的工業互聯網: https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA 同時,你也可以查看更多案例及效果: https://www.hightopo.com/demos/index.html 本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

系統梳理一下鎖_網頁設計公司

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

背景

有人對Java主流鎖做了下面全面的梳理。梳理的確實挺好的。但是我看到這張圖,第一個感覺是:記不住。

  

因為分了太多類,彼此之間沒有什麼聯繫。做PPT可以。如果聊天或者面試,不用紙筆的情況下,就不太好描述了。也不利於對原理和應用的理解。

基於上述的考慮,我就自己系統的梳理一下鎖,希望可以有助於大家理解和記憶,以至於最後在工作中得到很好的應用。

先說線程鎖再說分佈式鎖。

 

線程鎖

概述 

這裏說的線程鎖是Java線程鎖,從原理上各個語言應該都比較相似。有很多維度的劃分方式,我比較建議的是從大面上分為樂觀鎖和悲觀鎖。

樂觀鎖主要是自旋+CAS的方式,比如JUC(java.util.concurrent包)的原子類。 

悲觀鎖主要用synchronized關鍵字的隱式鎖和基於AQS的显示鎖。

上面三段總結如下:

 

悲觀鎖的實現原理

1>synchronized關鍵字

隨着java版本升級,synchronized關鍵字雖然是用C++寫的,但是原理和JCU包的ReentrantLock很相似。synchronized關鍵字有4種鎖狀態:無鎖、偏向鎖、輕量級鎖、重量級鎖。無鎖類似於ReentrantLock的交替執行,沒有併發,就不涉及鎖;偏向鎖類似於ReentrantLock的可重入的概念,使得已經獲取到鎖的線程可以多次獲取鎖;輕量級鎖解決的問題是盡量避免線程切換,使用的方法也和ReentrantLock相似,是自旋+CAS的方式;重量級鎖依賴於管程monitor來實現,和ReentrantLock一樣都涉及用戶態和內核態切換。

根據這個我們再來補充一下Java線程鎖的思維導圖:

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

 

2>基於基於AQS的显示鎖

基於AQS的显示鎖我之前看過一些源碼。這裏面比較經典的是ReentrantLock。這是可重入鎖,就是同一個線程可以反覆進入加鎖的線程。如果想實現不可重入鎖也很簡單。把可重入鎖對當前線程做特殊處理的部分去掉就好了。

其他JCU下locks包里的鎖比如讀寫鎖就是將鎖細化成了讀鎖和寫鎖。讀鎖是共享鎖的實現,寫鎖是排他鎖的實現。

ReentrantLock可以使用公平鎖和非公平鎖兩種方式,公平鎖和非公平鎖各自繼承了AQS。區別只是非公平鎖在需要加鎖時先直接嘗試是否可以獲取鎖成功,而公平鎖是先看自己是否需要排隊。

下面以ReentrantLock的公平鎖為例來簡單聊一下AQS的源碼。AQS核心是實現了CLH隊列。

AQS有head、tail、持有鎖的線程、狀態4個主要的成員變量。

利用head!=tail就是說AQS是否未被初始化來判斷是否交替執行,交替執行則不用加鎖;如果需要加鎖則判斷是否就是當前擁有鎖的線程,是的話,將進入次數+1;如果不是則判斷是否需要初始化AQS,需要的話先初始化一個dummy header,再將自己加入隊尾,如果是隊列里dummy header的指針指向的節點,則它為先自旋判斷是否可以獲取鎖;如果不是dummy header指針指向的節點,則使用park讓出cpu。當dummy header的指針指向的節點獲取到鎖之後,會將head指向自己,同時將自己這個Node節點的當前線程設置為空,將自己設置為dummy header,同時將原來dummy header的指針都設置為null,使得原dummy header成為一個沒有引用的節點,便於垃圾回收。

根據這個我們再來補充一下Java線程鎖的思維導圖:

 

 

 

 

分佈式鎖

不管是線程鎖還是分佈式鎖,都實現了tryLock、lock、unlock三個方法。

tryLock的語義是非阻塞鎖,嘗試獲取鎖,成功返回true,不成功返回false;主流lock語義是阻塞鎖。實現一般基於tryLock來做自旋,不成功的時候也會有像ReentrantLock一樣的阻塞操作。

常見的分佈式鎖實現以及數據庫鎖的實現詳見之前寫的文章:《MySQL常見6個考題在實際工作中的運用》這裏就不再贅述了。

 

總結

本篇文章在介紹知識點是次要的,主要是展示了總結思考的思路,希望能對讀者朋友們的思考問題方法上有所幫助,僅做參考。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

子彈真的能轉彎!Nerf 新玩具槍能射出《刺客聯盟》超炫槍法_網頁設計公司

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

雖然電影《刺客聯盟》最後由安潔莉納裘莉所尻射出,射爆一整圈人的終極槍法可能還是無法被複製達成。但故事裡刺客所擁有讓子彈轉彎射中遮蔽物後物體的神技巧,現在也能透過玩具槍以特殊的機制實現。繼續閱讀子彈真的能轉彎!Nerf 新玩具槍能射出《刺客聯盟》超炫槍法報導內文。

▲圖片來源:孩之寶

子彈真的能轉彎!Nerf 新玩具槍能射出《刺客聯盟》超炫槍法

不得不說孩之寶的 Nerf 玩具槍的確相當有梗,不僅有包括 D.Va 光槍的各種造型聯名,還能讓玩具槍的性能有超炫的變化 — 嗯,應該說是超「旋」的變化。

▲圖片來源:孩之寶

他們最新的 Rival Curve 系列就如其名,是款可以透過玩具槍口原本應該是做為造型為主的砲口制動器,透過特殊的轉動切換機制(頂端的瞄準器也會跟著轉 XD),讓使用者可以切換射擊彈道的「曲度」,達成類似《刺客聯盟》那樣讓子彈轉彎的有趣玩法。

Rival Curve 共有三種款式的軟球彈玩具槍,除了造型上的差異,比較接近手槍的 FLEX XXI-100 是採用單發設計,軟球彈則是可被收納在手把前方;其餘版本則是可以填充十顆以上軟球彈。雖說感覺孩之寶官方示範的 CG 曲度有點誇張,但 YouTube 已經有人玩到,看來「曲球」效果真的是有的!可以看看。

Nerf Rival Curve 的價格則是最便宜的版本可以用 US$15(約新台幣 500)入手,並且已經在美國開賣。不知道進台灣會要多少錢?如果真的可以這麼便宜的話,好像真的可以買來玩玩,看能不能重現《刺客聯盟》電影裡的那種超神轉彎槍法啊!

▲圖片來源:Universal Pictures (YouTube)

本篇圖片 / 引用來源

延伸閱讀:

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

曲面感光元件版 RX1 會是 Sony Alpha 發表會的重磅新品?

MagSafe 會干擾心律調節器?蘋果支援頁面有正式解答了

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

Chrome 開始在共用螢幕畫面時將瀏覽器通知靜音處理,避免個人隱私被看光光_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

從疫情爆發之後,遠端工作與虛擬會議已經成為工作與生活中的常態,而在會議中當然不只有談話,有些更正是的視訊會議裡面你還必須共享電腦螢幕畫面上,以進行操作與成果的展示等,如果有這樣的需求,相信大家會想方設法來讓自己的桌面看起來更乾淨美觀,但總會有些無法控制的通知跳出來,被全部看光光好尷尬呀!Google 現在為了這些不受控的事情,為 Chrome 導入了一個小卻很重要的功能。

Chrome 開始在共用螢幕畫面時將瀏覽器通知靜音處理,避免個人隱私被看光光

任何參加過視訊會議或進行線上直播展示的人可能都遇過,當你的展示一切都很順利,眼看就可以完美收官了,直到你的瀏覽器分頁之一收到通知,大咧咧地彈出在所有人面前「雅俗共賞」,這一情況不僅讓與會人士分心,當訊息內容比較私密、個人的時候,還會成為隱私攤在所有人眼前的問題。

當然,你可以直接選擇禁用瀏覽器通知,但這通常需要進行很多步驟來設定,而且還要記得在共用螢幕之前記得去設定,在結束共用之後再重新開啟通知,不過光是螢幕共用展示的身心準備已經很緊張,大多數人可能並不記得採取這些預防措施。Chrome 瀏覽器現在將幫你拾遺補漏,當你開始共用螢幕時,它會通知你接下來將自動令瀏覽器進入靜音狀態,且在你的螢幕共用時隱藏通知內容。當你結束共用螢幕後,所有之前被靜音的通知都將會在後續顯示出來。

通知靜音功能不僅適用於 Google 自家應用程式(如 Gmail 或 Google Chat 等),也適用於第三方網站,最重要的是此功能面向所有人推出,無論你是商務企業方案訂閱戶或是個人 Google 帳戶都能擁有,就藏在 Chrome 88 版中。

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

◎資料來源:9to5Google、SlashGear

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

要價近 7 萬的 Xperia PRO 正式推出:Sony Alpha 1 無反機皇最專業(DLC)拍檔_網頁設計公司

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

好了好了,別再說近 20 萬 Alpha 1 螢幕不能翻轉解析度又低是哪招。秉持著 One Sony 精神,Xperia 立馬就來了個同集團最強手機與相機的強強結合 — 如同預測的那樣,Xperia PRO 在具備 8K 錄影機能的 A1 發表時也隨之正式推出。將可透過 HDMI 外接的方式,成為史上最強的 4K HDR OLED 監視螢幕 — 嗯… 再加上 5G mmWave 連線應該算吧?不然… 你有看過搭載 3 主相機 + 3D iTOF 的監視器嗎!繼續閱讀要價近 7 萬的 Xperia PRO 正式推出:Sony Alpha 1 無反機皇最專業(DLC)拍檔報導內文。

▲圖片來源:

要價近 7 萬的 Xperia PRO 正式推出:Sony Alpha 1 無反機皇最專業(DLC)拍檔

全面迎擊對手挑戰,帶來 8K 30fps 與 4K 120fps 錄影機能以及 50MP 高畫質、30fps 高速連拍,以「One」之名成為 Alpha 全片幅無反最高階存在的 A1。想不到還是承襲了 Sony 無反螢幕規格總是稍微弱了些的 DNA,甚至也沒有配上 A7S III 的全翻轉螢幕。實在是讓你有點失望?

其實 Sony 早早在去年就宣佈了配合 A1 的解決方案,將為專業而生的旗艦手機 Xperia PRO 在專業相機的發表會場合正式推出,宣布了上市資訊與相關規格。

Xperia PRO 搭載有支援超越 PCI-P3 廣色域,並與專業級 HDR 監控螢幕 Sony BVM-X300 採相同 gamut 係數的 6.5 吋 4K HDR OLED 螢幕。有望成為專業攝錄工作流程中相當輕便但強大的工具。在這次 Alpha 1 全幅無反機皇的發表會裡,Sony 也釋出其 US$2,500 的售價(!)並確認將正式在海外開賣。

既然這是台換算近 7 萬台幣的「手機」,大家應該完全可以感受到它的確並不是為一般人而來的產品。近年致力於為創作者打造核心體驗的 Sony 旗艦手機,現在,純粹為專業而生的 Xperia PRO 也進化登場。

之所以敢訂出這樣專業級的售價,Xperia PRO 自然有著專業用戶會想要買單的規格。支援觸控放大的 4K 60fps HDR 監視螢幕的用途應該不用再特別說了。它不僅可以透過 micro HDMI 外接,輸入影像進行最高 131Mbps 的串流直播,並能以 5G 毫米波搭配四組 360 度全向天線的強大連線規格全力保證通訊的穩定。

內建 3.5mm 耳機孔、雙前置揚聲器也支援杜比全景聲的它,不僅可以進行即時的影音回放,也能直接做為檔案傳輸中心,即時將內容透過 5G 傳輸至 FTP 伺服器。配合 Alpha 1 更進階的耐候規格,Xperia PRO 也擁有 IP65/68 的耐候能力,加上看起來相當強壯的機身設計,想必未來應該可以在不少專業製片領域看到它的蹤跡才是。

本篇圖片 / 引用來源

延伸閱讀:

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

能錄 8K 的 Sony Alpha 1 全幅無反全能機皇正式發表

M1 MacBook Air 開箱體驗:最驚喜的不變

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

萬字總結之反射(框架之魂)_網頁設計公司

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

前言

準備過年看下Spring源碼,用來唬人,哈哈哈哈。正經點,是為了在遇到問題的時候,能知其然而知其所以然。但是在開始前,先惡補下基礎知識。今天看框架之魂——反射。

反射的概述(基礎部分開始)

反射是在編譯狀態,對某個類一無所知 ,但在運行狀態中,對於任意一個類,都能知道這個類的所有屬性和方法。

這個說太乾澀了,沒有靈魂,就像下面兩張圖。

所以咱來舉個例子,拒絕沒有靈魂。O(∩_∩)O哈哈~

為什麼要反射?

如果我們沒有Orange類,那該類在編譯的時候就會報錯找不到該類。這是我們平常使用的“正射”。這個名字是為了和反射相對應,不是官方的術語。

但是這存在着一個明顯的缺點,就是在main方法里調用的是Apple類,並沒有調用Orange類,所以應該是可以正常調用的,當我想要調用Orange類的時候,再報錯即可。但是,事與願違,事情不是照着我們的想法而發展的。

我們需要一種在編譯時不檢查類的調用情況,只有在運行時,才根據相應的要求調用相應的類,這就是“反射”。

反射的用途

反射最重要的用途就是開發各種通用框架。很多框架(比如 Spring)都是配置化的(比如通過 XML 文件配置 Bean),為了保證框架的通用性,它們可能需要根據配置文件加載不同的對象或類,調用不同的方法,這個時候就必須用到反射,運行時動態加載需要加載的對象。

舉一個例子,在運用 Struts 2 框架的開發中我們一般會在 struts.xml 里去配置 Action,比如:

<action name="login"       
        class="org.ScZyhSoft.test.action.SimpleLoginAction"   
        method="execute">      
      <result>/shop/shop-index.jsp</result>     
      <result name="error">login.jsp</result>
</action> 

配置文件與 
Action 建立了一種映射關係,當 View 層發出請求時,請求會被 
StrutsPrepareAndExecuteFilter 攔截,然後 
StrutsPrepareAndExecuteFilter 會去動態地創建 Action 實例。比如我們請求 
login.action,那麼 
StrutsPrepareAndExecuteFilter就會去解析struts.xml文件,檢索action中name為login的Action,並根據class屬性創建SimpleLoginAction實例,並用invoke方法來調用execute方法,這個過程離不開反射。

獲取Class文件對象的三種方式

萬事萬物都是對象。

Apple apple=new Apple();中的apple為Apple的一個實例。那Apple對象是哪個的實例呢?

其實是Class類的實例。

我們可以看他的註釋,私有的構造方法,只有JVM才能創建對象。

如果我們能找到某個對象的Class類,即可以創建其實例。

  • 靜態屬性class
  • Object類的getClass方法,如果知道實例,直接調用其getClass方法。
  • Class類的靜態方法forName(),參數為類的完整路徑(推薦使用)

這裏需要注意,通過類的全路徑名獲取Class對象會拋出一個異常,要用try….catch…捕獲異常。如果根據類路徑找不到這個類那麼就會拋出這個異常,Class類中forName方法源碼如下:

注:雖然寫了三種方式,但平常使用最多,最推薦的是第三種方式,因為第一種方式需要知道類,第二種方式需要知道實例,如果知道了這些,可以直接調用其方法和參數,沒必要再用Class來實現功能。舉個例子,你從北京去上海,第一種方式直達就行,第二種方式和第三種方式則是先從北京到雲南,再從雲南到上海,顯得太冗餘。

反射的使用

我們以Apple類為例,利用發射來獲取其參數和方法。其有三個參數,默認default參數color,公有public參數size,私有private參數price。三個構造方法,分別是默認default構造,公有public帶有三個參數的有參構造,私有帶有兩個參數的有參構造。六個setter/getter方法公有方法,分別是color的默認default隔離級別的setter/getter方法,size的public隔離級別的setter/getter方法,price的private隔離級別的setter/getter方法。toString和三個參數的setter/getter方法。最後還有一個public隔離級別的toString方法。這樣詳細展開的描述,看起來很複雜,其實很簡單的,具體代碼如下:

package com.eastrobot.reflect;

public class Apple extends Fruit{
    String color;//默認default
    public int size;
    private int price;

    Apple() {//默認default
        System.out.println("Apple的無參構造");
    }

    public Apple(String color, int size, int price) {
        this.color = color;
        this.size = size;
        this.price = price;
        System.out.println("Apple的有參構造——三個參數");
    }

    private Apple(String color, int size) {
        this.color = color;
        this.size = size;
        this.price = 10;
        System.out.println("Apple的有參構造——兩個參數");
    }

    @Override
    public String toString() {
        return "color:" + color + ",size:" + size + ",price:" + price;
    }

    //默認default
    String getColor() {
        return color;
    }

    public int getSize() {
        return size;
    }

    private int getPrice() {
        return price;
    }

    //默認default
    void setColor(String color) {
        this.color = color;
    }

    public void setSize(int size) {
        this.size = size;
    }

    private void setPrice(int price) {
        this.price = price;
    }
}

 

繼承的父類Fruit,包括一個public類型的參數taste,和其public類型的setter/getter方法。

public class Fruit {   
 public String taste; 
 public String getTaste() {  
      return taste;    
 }   
 public void setTaste(String taste) {   
     this.taste = taste;  
  }
}

 

1.通過反射獲取所有參數 getDeclaredFields

System.out.println("getDeclaredFields**********");
Field[] fields=appleClass.getDeclaredFields();
for(Field field:fields){   
 System.out.println(field.toString());
}

 

運行結果如下:

注:不管何種隔離級別,getDeclaredFields都會獲取到所有參數。

2.通過反射獲取指定參數getDeclaredField

//指定參數
System.out.println("getDeclaredField**********");
Field colorField=appleClass.getDeclaredField("color");
System.out.println("color:"+colorField.toString());

Field sizeField=appleClass.getDeclaredField("size");
System.out.println("size:"+sizeField.toString());

Field priceField=appleClass.getDeclaredField("price");
System.out.println("price:"+priceField.toString());

 

運行結果如下:

注:不管何種隔離級別,getDeclaredField可以通過輸入值獲取指定參數。

3.通過反射獲取所有pubic類型的參數 getFields

System.out.println("getFields**********");
Field[] fields=appleClass.getFields();
for(Field field:fields){    
    System.out.println(field.toString());
}

 

運行結果如下:

注:只能通過反射獲取public類型的屬性,也包括繼承自父類的屬性。

4.通過反射獲取指定public類型的參數 getField

Field colorField=appleClass.getField("color");
System.out.println("color:"+colorField.toString());

 

運行結果如下:

——————-手動分割線——————-

Field sizeField=appleClass.getField("size");
System.out.println("size:"+sizeField.toString());

 

運行結果如下:

——————-手動分割線——————-

Field priceField=appleClass.getField("price");
System.out.println("price:"+priceField.toString());

 

運行結果如下:

注:只有public類型才能通過getField方法獲取到,其他類型均獲取不到。

看到這裏,有些小夥伴要問了,這是為啥,理由呢?咱不能死記硬背,這樣過两天就忘了,記得不牢固,咱來瞅瞅底層幹了啥。

插曲:為什麼getFields和getField只能獲取public類型的字段?

我們以getField為例,觀察getDeclaredField和getField的區別,可以看到兩者都調用了privateGetDeclaredFields方法,但是區別是getDeclaredField方法中的參數publicOnly是false,getField方法中的參數publicOnly為true。

getDeclaredField方法:

getField方法:

那privateGetDeclaredFields裏面幹了啥,我們看下。

我們可以看到如果為true,就取declaredPublicFields字段,即public字段,如果為false,就取DeclaredFields。

5.通過反射獲取所有方法 getDeclaredMethods

//所有方法
System.out.println("getDeclaredMethods**********");
Method[] methods=appleClass.getDeclaredMethods();
for(Method method:methods){    
    System.out.println(method.toString());
}

 

運行結果如下:

6.通過反射獲取指定方法 getDeclaredMethod

//指定方法
System.out.println("getDeclaredMethod**********");

//default
Method getColorMethod=appleClass.getDeclaredMethod("getColor");
System.out.println("getColorMethod:"+getColorMethod.toString());

//public
Method getSizeMethod=appleClass.getDeclaredMethod("getSize");
System.out.println("getSizeMethod:"+getSizeMethod.toString());

//private
Method getPriceMethod=appleClass.getDeclaredMethod("getPrice");
System.out.println("getPriceMethod:"+getPriceMethod.toString());

//父類的public
Method getTasteMethod=appleClass.getDeclaredMethod("getTaste");
System.out.println("getTasteMethod:"+getTasteMethod.toString());

 

運行結果如下:

注:getDeclaredMethod只能獲取自己定義的方法,不能獲取從父類的方法。

7.通過反射獲取所有public類型的方法 getMethods

//所有方法
System.out.println("getMethods**********");
Method[] methods=appleClass.getMethods();
for(Method method:methods){
    System.out.println(method.toString());
}

 

運行結果如下:

注:getMethods可以通過反射獲取所有的public方法,包括父類的public方法。

8.通過反射獲取指定public類型的方法 getMethod

//指定方法
System.out.println("getMethod**********");
Method method=appleClass.getMethod("toString");
System.out.println(method.toString());

 

運行結果如下:

9.通過反射獲取所有構造方法 getDeclaredConstuctors

//構造方法
System.out.println("getDeclaredConstructors**********");
Constructor[] constructors=appleClass.getDeclaredConstructors();
for(Constructor constructor:constructors){   
 System.out.println(constructor.toString());
}

 

運行結果如下:

10.通過反射獲取某個帶參數的構造方法 getDeclaredConstructor

//指定構造方法
System.out.println("getDeclaredConstructor**********");
Class[] cArg = new Class[3];
cArg[0] = String.class;
cArg[1] = int.class;
cArg[2] = int.class;
Constructor constructor=appleClass.getDeclaredConstructor(cArg);
System.out.println(constructor.toString());

 

運行結果如下:

11.通過反射獲取所有public類型的構造方法getConstructors

System.out.println("getConstructors**********");
Constructor[] constructors=appleClass.getConstructors();
for(Constructor constructor:constructors){
    System.out.println(constructor.toString());
}

 

運行結果:

12.通過反射獲取某個public類型的構造方法getConstructor

//構造方法
System.out.println("getConstructor**********");
Constructor constructor1 = appleClass.getConstructor(String.class,int.class,int.class);
System.out.println("public類型的有參構造:" + constructor1.toString());

Constructor constructor2 = appleClass.getConstructor(String.class, int.class);
System.out.println("private類型的有參構造:" + constructor2.toString());

 

運行結果:

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

13.通過無參構造來獲取該類對象 newInstance()

//調用無參構造創建對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");
Apple apple=(Apple)appleClass.newInstance();

 

運行結果如下:

14.通過有參構造來獲取該類對象 newInstance(XXXX)

Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class);
Apple apple=(Apple)constructor.newInstance("紅色",10,5);

 

運行結果如下:

15.獲取類名包含包路徑 getName()

String name= appleClass.getName();
System.out.println("name:"+name);

 

運行結果如下:

16.獲取類名不包含包路徑 getSimpleName()

String simpleName =appleClass.getSimpleName();
System.out.println("simpleName:"+simpleName);

 

運行結果如下:

17.通過反射調用方法 invoke

//調用無參構造創建對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");

//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);

//獲取toString方法並調用
Method method = appleClass.getDeclaredMethod("toString");
String str=(String)method.invoke(apple);
System.out.println(str);

 

注:invoke+實例可以調用相關public方法。

18.判斷方法是否能調用isAccessible

//調用無參構造創建對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");

//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);

//獲取public的getSize方法並調用
Method method = appleClass.getDeclaredMethod("getSize");
System.out.println("getSize方法的isAccessible:" + method.isAccessible());
int size = (Integer) method.invoke(apple);
System.out.println("size:" + size);

//獲取private的getPrice方法並調用
method = appleClass.getDeclaredMethod("getPrice");
System.out.println("getPrice的isAccessible:" + method.isAccessible());
int price = (Integer) method.invoke(apple);
System.out.println("price:" + price);

 

運行結果:

注:這樣一看,public和private類型的isAccessible都為false,但是public類型的值可以獲取到,但是private類型的值並不能獲取到。其實isAccessible()值為 true或false,是指啟用和禁用訪問安全檢查的開關,如果為true,則取消安全檢查,為false,則執行安全檢查。如上,兩者都為false,說明兩者的進行了安全檢查,getSize為public類型,則可以獲取值,而getPrice為private,則不能獲取值。

19.設置安全檢查開關setAccessible

//調用無參構造創建對象
Class appleClass = Class.forName("com.eastrobot.reflect.Apple");

//調用有參構造
Constructor constructor = appleClass.getDeclaredConstructor(String.class, int.class, int.class);
Apple apple = (Apple) constructor.newInstance("紅色", 10, 20);

//獲取price
Method otherMethod = appleClass.getDeclaredMethod("getPrice");
System.out.println("getPrice方法的isAccessible:" + otherMethod.isAccessible());
otherMethod.setAccessible(true);
int price = (Integer) otherMethod.invoke(apple);
System.out.println("之前的price:" + price);

//重新設置price
Method method = appleClass.getDeclaredMethod("setPrice", int.class);
System.out.println("isAccessible:" + method.isAccessible());
method.setAccessible(true);
method.invoke(apple, 100);

//再次獲取price
otherMethod = appleClass.getDeclaredMethod("getPrice");
otherMethod.setAccessible(true);
price = (Integer) otherMethod.invoke(apple);
System.out.println("之後的price:" + price);

 

運行結果:

注:setAccessible(true)表示取消安全檢查,setAccessible(false)表示啟用安全檢查。

常見面試題解答(進階部分開始)

被反射的類是否一定需要無參構造方法?

不一樣。因為有參構造方法也可以反射,具體代碼如下:

Constructor constructor=appleClass.getConstructor(String.class,int.class,int.class);
Apple apple=(Apple)constructor.newInstance("紅色",10,5);

 

反射的使用有什麼優勢和劣勢?

優勢:

在編譯時根本無法知道該對象或類可能屬於哪些類,程序只依靠運行時信息來發現該對象和類的真實信息。反射提高了Java程序的靈活性和擴展性,降低耦合性,提高自適應能力。它允許程序創建和控制任何類的對象,無需提前硬編碼目標類。

劣勢:

使用反射基本上是一種解釋操作,用於字段和方法接入時要遠慢於直接代碼

使用反射會模糊程序內部邏輯,程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術,因而會帶來維護問題。(這也就是看源碼為什麼這麼難?哎。。。。)

為什麼說反射可以降低耦合?

因為反射不是硬編碼,在運行時可以靈活發現該類的詳細信息,降低了代碼之間的耦合性。

反射比較損耗性能,為什麼這樣說?(重點)

怎麼去判斷一個函數的性能?因為函數的執行太快太快了,你需要一個放慢鏡,這樣才能捕捉到他的速度。怎麼做?把一個函數執行一百萬遍或者一千萬遍,你才能真正了解一個函數的性能。也就是,你如果想判斷性能,你就不能還停留在秒級,毫秒級的概念。

如下是將直接獲取實例,直接獲取方法,反射獲取實例,反射獲取方法分別執行1百萬次所花費差。

try {    
    //直接獲取實例
    long startTime1 = System.currentTimeMillis();   
    for (int i = 0; i < 1000000; i++) { 
       new Apple();   
    }  
    long endTime1 = System.currentTimeMillis();  
    System.out.println("直接獲取實例時間:" + (endTime1 - startTime1));   
 
    //直接獲取方法  
    long startTime2= System.currentTimeMillis();   
    for (int i = 0; i < 1000000; i++) {  
      new Apple().toString();   
    }  
    long endTime2 = System.currentTimeMillis();  
    System.out.println("直接獲取方法時間:" + (endTime2- startTime2)); 
   
   //反射獲取實例  
   Class appleClass=Class.forName("com.eastrobot.reflect.Apple");   
   long startTime3 = System.currentTimeMillis();   
   for (int i = 0; i < 1000000; i++) {
       appleClass.getDeclaredConstructor().newInstance();  
    }   
   long endTime3 = System.currentTimeMillis(); 
   System.out.println("反射獲取實例:" + (endTime3 - startTime3));   
 
   //反射獲取方法  
   Apple apple= (Apple)appleClass.getDeclaredConstructor().newInstance();   
   long startTime4 = System.currentTimeMillis();   
   for (int i = 0; i < 1000000; i++) {    
     Method method=appleClass.getMethod("toString");    
     method.invoke(apple);   
   }    
   long endTime4 = System.currentTimeMillis();  
   System.out.println("反射獲取方法:" + (endTime4 - startTime4));

 

運行結果截圖:

我們可以看到反射的確會導致性能問題,但反射導致的性能問題是否嚴重跟使用的次數有關係,如果控制在100次以內,基本上沒什麼差別,如果調用次數超過了100次,性能差異會很明顯。

打個比方,如果快遞員就在你住的小區,那麼你報一個地址:xx棟xx號,那麼快遞員就可以馬上知道你在哪裡,直接就去到你家門口;但是,如果快遞員是第一次來你們這裏,他是不是首先得查查百度地圖,看看怎麼開車過去,然後到了小區是不是得先問問物管xx棟怎麼找,然後,有可能轉在樓下轉了兩個圈才到了你的門前。

我們看上面這個場景,如果快遞員不熟悉你的小區,是不是會慢點,他的時間主要花費在了查找百度地圖,詢問物業管理。OK,反射也是一樣,因為我事先什麼都不知道,所以我得花時間查詢一些其他資料,然後我才能找到你。

綜上,大部分我們使用反射是不考慮性能的,平常使用的次數較少,如果真的遇到性能問題,如反射的效率影響到程序邏輯,可以採用緩存或Java字節碼增強技術,參照庫有asm,也有第三方工具庫reflectAsm(https://github.com/EsotericSoftware/reflectasm)。

反射中的setAccessible()方法是否破壞了類的訪問規則

setAccessible(true)取消了Java的權限控制檢查(注意不是改變方法或字段的訪問權限),對於setAccessible()方法是否會破壞類的訪問規則,產生安全隱患,見下:

反射源碼解析

我們跟進Method的invoke方法,分為兩步,一是語言訪問級別是否為重寫,如果不是重寫則調用Reflection的quickCheckMemberAccess方法,即通過Modifiers 判斷是否具有訪問權限,quickCheckMemberAccess方法主要是簡單地判斷 modifiers 是不是 public,如果不是的話就返回 false。所以 protected、private、default 修飾符都會返回 false,只有 public 都會返回 true。如果為false,則調用checkAccess方法。二是獲取MethodAccessor對象,並調用其invoke方法。

public final class Method extends AccessibleObject implements GenericDeclaration, Member {   
     private volatile MethodAccessor methodAccessor; 
    //每個Java方法只有一個對應Method對象作為root,這個root不會暴露給用戶,
    //而是每次通過反射獲取Method對象時新創建的Method對象將root包裝起來。
     private Method   root;

    @CallerSensitive 
    public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;
        //在第一次調用一個實際Java方法應該的Method對象的invoke方法之前
        //實現調用邏輯的MethodAccessor對象還沒有創建
        //等到第一次調用時才創建MethodAccessor,並通過該MethodAccessor.invoke真正完成反射調用
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        //invoke並不是自己實現的反射調用邏輯,而是委託給sun.reflect.MethodAccessor來處理
        return ma.invoke(obj, args);
    } 

    ...

     private MethodAccessor acquireMethodAccessor() {
        MethodAccessor tmp = null;
        if (root != null) tmp = root.getMethodAccessor();
        if (tmp != null) {
            methodAccessor = tmp;
        } else {
            //調用ReflectionFactory的newMethodAccessor方法,見下
            tmp = reflectionFactory.newMethodAccessor(this);
            //更新root,以便下次直接使用
            setMethodAccessor(tmp);
        }
        return tmp;
    }

    ...

     void setMethodAccessor(MethodAccessor accessor) {
        methodAccessor = accessor;
        // Propagate up
        if (root != null) {
            root.setMethodAccessor(accessor);
        }
    }

 

Reflection類:

public static boolean quickCheckMemberAccess(Class<?> memberClass,
                                                 int modifiers)
{
    return Modifier.isPublic(getClassAccessFlags(memberClass) & modifiers);
} 

 

ReflectionFactory類:

 

private static boolean noInflation = false;
//選擇java版還是C語言版的閾值
private static int inflationThreshold = 15;

public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
        if (noInflation) {
            //java版
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            //c語言版
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
    }

如上述代碼所示,實際的MethodAccessor實現有兩個版本,一個是Java實現的,另一個是native code實現的。Java實現的版本在初始化時需要較多時間,但長久來說性能較好;native版本正好相反,啟動時相對較快,但運行時間長了之後速度就比不過Java版了。這是HotSpot的優化方式帶來的性能特性,同時也是許多虛擬機的共同點:跨越native邊界會對優化有阻礙作用,它就像個黑箱一樣讓虛擬機難以分析也將其內聯,於是運行時間長了之後反而是託管版本的代碼更快些。

為了權衡兩個版本的性能,Sun的JDK使用了“inflation”的技巧:讓Java方法在被反射調用時,開頭若干次使用native版,等反射調用次數超過閾值時則生成一個專用的MethodAccessor實現類,生成其中的invoke()方法的字節碼,以後對該Java方法的反射調用就會使用Java版。
Sun的JDK是從1.4系開始採用這種優化的,主要作者是Ken Russell

MethodAccessor的C語言實現(默認)

C語言版的MethodAccessor主要涉及這NativeMethodAccessorImpl和DelegatingMethodAccessorImpl兩個類,而DelegatingMethodAccessorImpl是間接層,不是太重要,就不貼代碼啦。以下是NativeMethodAccessorImpl的代碼,核心是invoke方法:

 

class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private final Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;

    NativeMethodAccessorImpl(Method var1) {
        this.method = var1;
    }

    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        if (++this.numInvocations > ReflectionFactory.inflationThreshold()) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }

        return invoke0(this.method, var1, var2);
    }

    void setParent(DelegatingMethodAccessorImpl var1) {
        this.parent = var1;
    }

    private static native Object invoke0(Method var0, Object var1, Object[] var2);
}

 

 

 

 


每次NativeMethodAccessorImpl.invoke()方法被調用時,都會增加一個調用次數計數器,看超過閾值沒有;一旦超過,則調用MethodAccessorGenerator.generateMethod()來生成Java版的MethodAccessor的實現類,並且改變DelegatingMethodAccessorImpl所引用的MethodAccessor為Java版。後續經由DelegatingMethodAccessorImpl.invoke()調用到的就是Java版的實現了。

MethodAccessor的Java實現

 return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());

 


Java的MethodAccessor主要涉及的是MethodAccessorGenerator類,具體代碼超長,只截取了部分代碼,主要有三個方法,直接就是上述的generateMethod方法,代碼如下:

 

public MethodAccessor generateMethod(Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6) {
        return (MethodAccessor)this.generate(var1, var2, var3, var4, var5, var6, false, false, (Class)null);
    }
 private MagicAccessorImpl generate(final Class var1, String var2, Class[] var3, Class var4, Class[] var5, int var6, boolean var7, boolean var8, Class var9) {
        ByteVector var10 = ByteVectorFactory.create();
        this.asm = new ClassFileAssembler(var10);
        this.declaringClass = var1;
        this.parameterTypes = var3;
        this.returnType = var4;
        this.modifiers = var6;
        this.isConstructor = var7;
        this.forSerialization = var8;
        this.asm.emitMagicAndVersion();
        short var11 = 42;
        boolean var12 = this.usesPrimitiveTypes();
        if (var12) {
            var11 = (short)(var11 + 72);
        }

        if (var8) {
            var11 = (short)(var11 + 2);
        }

        var11 += (short)(2 * this.numNonPrimitiveParameterTypes());
        this.asm.emitShort(add(var11, (short)1));
        final String var13 = generateName(var7, var8);
        this.asm.emitConstantPoolUTF8(var13);
        this.asm.emitConstantPoolClass(this.asm.cpi());
        this.thisClass = this.asm.cpi();
        if (var7) {
            if (var8) {
                this.asm.emitConstantPoolUTF8("sun/reflect/SerializationConstructorAccessorImpl");
            } else {
                this.asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
            }
        } else {
            this.asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
        }

        this.asm.emitConstantPoolClass(this.asm.cpi());
        this.superClass = this.asm.cpi();
        this.asm.emitConstantPoolUTF8(getClassName(var1, false));
        this.asm.emitConstantPoolClass(this.asm.cpi());
        this.targetClass = this.asm.cpi();
        short var14 = 0;
        if (var8) {
            this.asm.emitConstantPoolUTF8(getClassName(var9, false));
            this.asm.emitConstantPoolClass(this.asm.cpi());
            var14 = this.asm.cpi();
        }

        this.asm.emitConstantPoolUTF8(var2);
        this.asm.emitConstantPoolUTF8(this.buildInternalSignature());
        this.asm.emitConstantPoolNameAndType(sub(this.asm.cpi(), (short)1), this.asm.cpi());
        if (this.isInterface()) {
            this.asm.emitConstantPoolInterfaceMethodref(this.targetClass, this.asm.cpi());
        } else if (var8) {
            this.asm.emitConstantPoolMethodref(var14, this.asm.cpi());
        } else {
            this.asm.emitConstantPoolMethodref(this.targetClass, this.asm.cpi());
        }

        this.targetMethodRef = this.asm.cpi();
        if (var7) {
            this.asm.emitConstantPoolUTF8("newInstance");
        } else {
            this.asm.emitConstantPoolUTF8("invoke");
        }

        this.invokeIdx = this.asm.cpi();
        if (var7) {
            this.asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
        } else {
            this.asm.emitConstantPoolUTF8("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
        }

        this.invokeDescriptorIdx = this.asm.cpi();
        this.nonPrimitiveParametersBaseIdx = add(this.asm.cpi(), (short)2);

        for(int var15 = 0; var15 < var3.length; ++var15) {
            Class var16 = var3[var15];
            if (!isPrimitive(var16)) {
                this.asm.emitConstantPoolUTF8(getClassName(var16, false));
                this.asm.emitConstantPoolClass(this.asm.cpi());
            }
        }

        this.emitCommonConstantPoolEntries();
        if (var12) {
            this.emitBoxingContantPoolEntries();
        }

        if (this.asm.cpi() != var11) {
            throw new InternalError("Adjust this code (cpi = " + this.asm.cpi() + ", numCPEntries = " + var11 + ")");
        } else {
            this.asm.emitShort((short)1);
            this.asm.emitShort(this.thisClass);
            this.asm.emitShort(this.superClass);
            this.asm.emitShort((short)0);
            this.asm.emitShort((short)0);
            this.asm.emitShort((short)2);
            this.emitConstructor();
            this.emitInvoke();
            this.asm.emitShort((short)0);
            var10.trim();
            final byte[] var17 = var10.getData();
            return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
                public MagicAccessorImpl run() {
                    try {
                        return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
                    } catch (InstantiationException var2) {
                        throw (InternalError)(new InternalError()).initCause(var2);
                    } catch (IllegalAccessException var3) {
                        throw (InternalError)(new InternalError()).initCause(var3);
                    }
                }
            });
        }
    } 
private static synchronized String generateName(boolean var0, boolean var1) {
        int var2;
        if (var0) {
            if (var1) {
                var2 = ++serializationConstructorSymnum;
                return "sun/reflect/GeneratedSerializationConstructorAccessor" + var2;
            } else {
                var2 = ++constructorSymnum;
                return "sun/reflect/GeneratedConstructorAccessor" + var2;
            }
        } else {
            var2 = ++methodSymnum;
            return "sun/reflect/GeneratedMethodAccessor" + var2;
        }
    }

 

 

 

 


去閱讀源碼的話,可以看到MethodAccessorGenerator是如何一點點把Java版的MethodAccessor實現類生產出來的,其實就是一個逐步解析的過程。

此時要注意的是最後的“sun/reflect/GeneratedMethodAccessor”+var2的代碼。

例子

以上空說無用,太乾澀,咱來個例子。

 

public class Foo {

public void foo(String name) {       
     System.out.println("Hello, " + name); 
   }
}

 

public class test {    
    public static void main(String[] args) {  
          try {          
                  Class<?> clz = Class.forName("com.eastrobot.reflect.Foo");      
                  Object o = clz.newInstance();  
                  Method m = clz.getMethod("foo", String.class);    
                  for (int i = 0; i < 17; i++) {        
                        m.invoke(o, Integer.toString(i));      
                  }       
         } catch (Exception e) {
         } 
   }
}

 

除了上述代碼,還需要在idea配置相關的運行參數,添加-XX:+TraceClassLoading參數,其為要求打印加載類的監控信息。

我們先用上述的例子執行下,運行結果如下,前面十五次是正常的,到第16次的時候,出現了很多打印信息,我已將一行標紅,“GeneratedMethodAccessor1”,這其實就是上面說的Java版獲取MethodAccessorGenerator的最後一行,1為自增參數。當第17次的時候,就不會用Java版的方式重新獲取,而是直接復用啦。

 

結語

終於結束了,邊玩邊寫,寫了五天,累死了,答應我,一定要好好看,好嗎?

如有說的不對地方,歡迎指正。

參考資料

Java反射詳細介紹

Java反射中getDeclaredField和getField的區別

java反射的使用場合和作用、及其優缺點

反射是否真的會讓你的程序性能降低?

深入解析Java反射(1) – 基礎

關於反射調用方法的一個log 

反射進階,編寫反射代碼值得注意的諸多細節

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

Protocol Buffers工作原理_網頁設計公司

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

這裏記錄一下學習與使用Protocol Buffer的筆記,優點缺點如何使用這裏不再敘述,重點關注與理解Protocol Buffers的工作原理,其大概實現。

我們經常使用Protocol Buffer進行序列化與反序列化。理解Protocol Buffer的工作原理,就要理解序列化與反序列化。

  • 序列化:將數據結構或對象轉換為二進制串的過程;
  • 反序列化:序列化的逆過程;

如何實現呢?核心有兩點:編碼 + 存儲。數據在計算機間通過網絡進行傳輸時,傳輸的是比特流,只有0和1,並沒有你所定義的各種類對象等,你如果想將一個類對象傳輸到對方,怎麼辦呢?字符是用過ASCII碼編碼的,這裏也可以設計一套編碼方案來對類似類對象這種數據進行編碼,只要對方收到后能正確的解碼就可以了。編碼后還要確定編碼后的數據存儲方式,這樣字節流才是有意義的字節流,這樣才能知道讀取的字節流有什麼含義,代表什麼。

好了,我們看一下Protocol Buffer是如何編碼和存儲的。

Protocol Buffer是如何編碼的

Varint編碼

Varint編碼是一種變長的編碼方式,核心思想是對數值越小的数字,用越少的字節數表示,這樣可以減少数字的字節數,進行數據壓縮。

舉個例子:對int數據類型,一般需要4個字節來表示,而實際上,對與數值較小的数字而言,無需這麼多字節,00000000 00000000 00000000 01111111 | 127,只需要一個字節就能表示,前面3個字節意義不大,浪費了許多空間。

當然,這種編碼並不是所有情況下都會變小,當數值非常大時,所需的字節會增多,但因為大多數情況下數值小的数字遠比數值大的多,所以整體看來,數據是被壓縮了的。

具體的,Varint編碼時,對每個字節的最高位賦予特殊含義:

  • 1:表示後序的字節也是該数字的一部分;
  • 0:表示這是最後一個字節,且剩餘7bit都用來表示数字(所以Varint解碼時,如果讀到最高位為0的字節時,就表示已經是Varing的最後一個字節);

因為每個字節的最高位都被佔用,用來表示特殊的含義,所以,當數值非常大時,原有的字節數就不夠用了,所以編碼時要增加字節數。

可以參考下圖加深理解:

編碼示例:

解碼示例:

還有一個問題:就是負數時怎麼辦?計算機中數值用補碼形式表示和存儲(負數在計算機中往往用最位1來表示負數,0表示正數,負數的補碼最高位為1),那按Varint編碼方式所有的負數都需要增加一個字節表示,這是不能被接受的,解決方法便是下面要講的zigzag編碼。

Zigzag編碼

Zigzag編碼是一種變長的編碼方式。zigzag按絕對值升序排列,將整數hash成遞增的數值序列,哈希函數為h(n)=(n<<1)^(n>>31),對應地long型(64)位的哈希函數為h(n)=(n<<1)^(n>>63)

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

n hex h(n) zigzag(hex)
0 00 00 00 00 00 00 00 00 00
-1 ff ff ff ff 00 00 00 01 01
1 00 00 00 01 00 00 00 02 02
-2 ff ff ff fe 00 00 00 03 03
2 00 00 00 02 00 00 00 04 04

可以看到,zigzag編碼將正數和負數重新映射為遞增的無符號數,其主要目的就是使絕對值小的數值映射為小的無符號數,已方便後序壓縮字節。

zigzag編碼還有很多細節,比如為了保證編碼的唯一可譯性,需對哈希值進行前綴碼編碼,這裏不再細述。

解碼為編碼的逆,首先將zigzag編碼還原成哈希值,然後用哈希函數h的逆h’(n)=(n>>>1)^-(n&1)得到原始整數值。

這裏只重點講Varint編碼和Zigzag編碼,像string、double等類型這裏不再描述,可參考文後的參考文檔。

T-L-V的數據存儲方式

T:tag,L:length,V:value。以標識-長度-字段值表示單個數據,最終將所有數據拼接成一個字節流,從而實現數據存儲的功能。

其中Length可選存儲,如存儲Varint編碼數據就不需要存儲Length,因為可根據每個字節最高bit位來判斷這個字節是不是該數據段的最後一個字節。

這裏重點說一下Tag。Tag用來標識字段,通過Tag能獲知這段字節流是屬於什麼類型數據的,其定義為:Tag = (field_number << 3) | wire_type
。這樣,解包時就可根據tag將value對應消息中的字段。

Tag佔用一個字節的長度(如果標識號超過了16,則多佔用一個字節的位置,原因是field_number左移了3位,編碼方式為Varint&Zigzag,編碼的時候一個字節不夠用了)。

message ChannelDataAck {
    bytes uuid = 1;     //這裏的1 、2就是field_number
    uint32 result = 2;  
}

Tag(字段標識號)在序列化和反序列化過程中非常重要。舉一個應用中非常常見的例子,在需要對原有結構進行增減字段的時候,同樣一個結構體定義,新版本代碼中對其增加了一個字段,那當新版本代碼序列化后給原有舊版本反序列化解析的時候,因為舊的沒有那個新增的字段,所以在解析時只解析自己有的字段,沒有的不進行解析,這樣舊的代碼依舊能從新字節流中解析出舊數據結構。那舊的數據結構的數據解析為新數據結構時,因為沒有新字段的數據,解析為新數據時該字段置為默認值。這樣就能保證兼容性,對協議升級較為友好。

可以看到通過T-L-V的數據存儲方式,能夠較好的解決字段不完全匹配時的如何解析的問題。

序列化 & 反序列化過程

序列化過程如下:

  1. 判斷每個字段是否有設置值,有值才進行編碼.
  2. 根據字段標識號&數據類型將字段值通過不同的編碼方式進行編碼.

反序列化過程如下:

  1. 解析從輸入流讀入的二進制字節數據流.
  2. 將解析出來的數據按照指定的格式讀取到C++、Rust等對應的結構類型中.

到這裏,基本把Protocol Buffers的工作原理簡單梳理了一遍,其他技術細節待以後再深究。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

【Java Spring Cloud 實戰之路】- 使用Nacos和網關中心的創建_網頁設計公司

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

0. 前言

在上一節中,我們創建了一個項目架構,後續的項目都會在那個架構上做補充。

1. Nacos

1.1 簡介

Nacos可以用來發現、配置和管理微服務。提供了一組簡單易用的特性集,可以快速實現動態服務發現、服務配置、服務元數據及流量管理。

Nacos用來更敏捷和容易地構建、交付和管理微服務平台。Nacos是構建以”服務“為中心的現代應用構架(例如微服務範式、雲原生範式)的服務基礎設置。

也就是通常我們所說的配置中心和服務發現中心。

1.2 搭建和啟動

Nacos目前版本不支持以Spring boot的形式創建服務,必須以一個Java包的形式單獨運行或者以Docker服務的形式運行,我們大概講解一下本地運行。

下載安裝包:

curl https://github.com/alibaba/nacos/releases/download/1.2.1/nacos-server-1.2.1.zip
unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz
cd nacos/bin

使用源碼安裝:

git clone https://github.com/alibaba/nacos.git
cd nacos/
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U  
ls -al distribution/target/

// change the $version to your actual path
cd distribution/target/nacos-server-$version/nacos/bin

啟動:

Linux/Unix/Mac

啟動命令(standalone代表着單機模式運行,非集群模式):

sh startup.sh -m standalone

如果您使用的是ubuntu系統,或者運行腳本報錯提示[[符號找不到,可嘗試如下運行:

bash startup.sh -m standalone

Windows

啟動命令:

cmd startup.cmd

或者雙擊startup.cmd運行文件。

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

2. Spring Cloud Gateway

整個的網關服務,我們採用的Spring Cloud Gateway。在Spring Cloud微服務里,整個系統只對外公開了網關,其他的服務是對外不可見的。所以需要設置一個讓我們可以用的網關服務。

在 nature/manager下創建一個gateway目錄,並添加pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>club.attachie</groupId>
        <artifactId>manager</artifactId>
        <version>${revision}</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>club.attachie</groupId>
    <artifactId>gateway</artifactId>
    <packaging>jar</packaging>
    <version>${revision}</version>

</project>

在manager下註冊該模塊:

<modules>
    <module>gateway</module>
</modules>

2.1 添加 Gateway

創建完成項目后,需要添加依賴包:

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-gateway -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

在gateway項目中,創建如下目錄:

├── pom.xml
└── src
    └── main
        ├── java
        │   └── club
        │       └── attachie
        │           └── gateway
        │               └── SpringGatewayApplication.java
        └── resources
            └── bootstrap.yml

創建 SpringGateAppliction.java文件,代碼如下:

package club.attachie.gateway;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;

/**
 * @author attaching
 */
@SpringBootApplication
@EnableDiscoveryClient
@RefreshScope
public class SpringGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringGatewayApplication.class, args);
    }
}

在resource目錄下創建 bootstrap.yml:

spring:
  application:
    name: gateway

yml 是Spring 的一種配置文件格式,其中名稱有application和bootstrap,bootstrap比application先加載。

2.2 添加 nacos

先在 nature/pom.xml 添加 nacos 版本號:

<nacos.version>2.2.1.RELEASE</nacos.version>

然後在dependencyManagement > dependencies 下添加 nacos相關依賴管理:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>${nacos.version}</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-starters</artifactId>
    <version>${nacos.version}</version>
</dependency>

在Gateway項目中pom.xml 添加:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

然後回過頭來,在bootstrap里設置:

spring:
  application:
    name: gateway

  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848

3 總結

nacos的配置和Gateway應用的介紹就到這裏為止了,因為個人並未對相關技術進行過多深入的研究,所以目前只能做到這些。後續研究深入了,會在這個系列中補齊的。

更多內容煩請關注我的博客《高先生小屋》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

記錄片串流服務 Documentary+ 推出,超過 150 部免費線上觀看_網頁設計公司

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

串流影音平台百家爭鳴,不管是電影、劇集、動畫,都能夠在用戶訂閱後利用串流方式來觀賞,也成為許多人休閒時光的消遣方式。近日一個專注於非電影類內容的平台 Documentary+ 上線推出了,希望能夠在競爭激烈中,成功在喜愛觀賞記錄片的人群中殺出一片紅海。

記錄片串流服務 Documentary+ 推出,超過 150 部免費線上觀看

Documentary+ 這個服務是由 Tony Hsieh(最近甫過世的前 Zappos 首席執行長)與 XTR 工作室合作的專案,並且由多位知名導演和電影製作人共同推出。一推出,該服務即有相當多樣化類別可供觀眾選擇,包括政治、體育、喜劇、音樂與真實犯罪,另外還有許多其他類型的記錄片,並且著重於高質感內容,一上線就有超過 150 部影片可供選擇觀賞。
【前往 Documentary+,點這裡】

Documentary+ 的創始人之一、XTR 首席執行長表示,平台上所有影片都是擁有合法授權,隨著影片的下架與新影片的推出,影片庫將隨著時間變化而變化,目的是為了讓整個服務始終保持個人化與高度規劃,就如同記錄片世界中的標準平台一樣。這項服務可以在電腦網頁、行動裝置、Apple TV、Amazon與 Roku 中觀看,目前為了能擴大使用族群採完全免費制且尚未有計劃推出無廣告版本,但未來若需求充足,該服務不排除討論無廣告套餐服務的可能。

雖說這個免費平台看起來相當強大,但 Documentary+ 目前還得面對相當多的競爭對手,包括 Netflix、HBO Max、Discovery+ 和 Hulu 都有自家產品,而像 Kanopy 和 CuriosityStream 這樣的服務也都有相當可觀的影片資源,不過這大多數服務都是需要按月收取訂閱費用(Kanopy 除外)。現階段免費提供服務,或許能夠在疫情期間為大家打發掉在家不出門的空閒,這時間點或許能夠幫助讓 Documentary+成為用戶訂閱付費服務外的重要備選,可惜的是目前該平台影片沒有字幕,更沒有中文字幕啊!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

◎資料來源:Gizmodo

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境