【Java】幾道讓你拿offer的知識點

来源:https://www.cnblogs.com/Java3y/archive/2018/08/02/9405093.html
-Advertisement-
Play Games

前言 只有光頭才能變強 之前在刷博客的時候,發現一些寫得比較好的博客都會默默收藏起來。最近在查閱補漏,有的知識點比較重要的,但是在之前的博客中還沒有寫到,於是趁著閑整理一下。 文本的知識點: Integer常量池 TCP拆包粘包 簡單區別 jdk1.6以後對Synchronize鎖優化 Java記憶體 ...


前言

只有光頭才能變強

之前在刷博客的時候,發現一些寫得比較好的博客都會默默收藏起來。最近在查閱補漏,有的知識點比較重要的,但是在之前的博客中還沒有寫到,於是趁著閑整理一下。

文本的知識點:

  • Integer常量池
  • TCP拆包粘包
  • select、poll、epoll簡單區別
  • jdk1.6以後對Synchronize鎖優化
  • Java記憶體模型

本文力求簡單講清每個知識點,希望大家看完能有所收穫

一、神奇的Integer

前陣子在群上看有人在討論關於Integer的true或者false問題,我本以為我已經懂了這方面的知識點了。但還是做錯了,後來去請教了一下朋友。朋友又給我發了另一張圖:

後來發現這是出自《深入理解Java虛擬機——JVM高級特性與最佳實踐(第2版)》中的10.3.2小節中~


public class Main_1 {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        System.out.println(c == d);
        System.out.println(e == f);
        System.out.println(c == (a + b));
        System.out.println(c.equals(a + b));
        System.out.println(g == (a + b));
        System.out.println(g.equals(a + b));
        System.out.println(g.equals(a + h));
    }

}

你們可以先思考一下再往下翻看答案,看看能不能做對。

1.1解題思路

在解這道題之前,相信很多人都已經知道了,在Java中會有一個Integer緩存池,緩存的大小是:-128~127

答案是:

  • true
  • false
  • true
  • true
  • true
  • false
  • true

簡單解釋一下:

  • 使用==的情況:
    • 如果比較Integer變數,預設比較的是地址值
    • Java的Integer維護了從-128~127的緩存池
    • 如果比較的某一邊有操作表達式(例如a+b),那麼比較的是具體數值
  • 使用equals()的情況:
    • 無論是Integer還是Long中的equals()預設比較的是數值
    • Long的equals()方法,JDK的預設實現:會判斷是否是Long類型
  • 註意自動拆箱,自動裝箱問題。

反編譯一下看看:


import java.io.PrintStream;

public class Main_1 {
    public static void main(String[] paramArrayOfString) {
        Integer localInteger1 = Integer.valueOf(1);
        Integer localInteger2 = Integer.valueOf(2);
        Integer localInteger3 = Integer.valueOf(3);
        Integer localInteger4 = Integer.valueOf(3);
        Integer localInteger5 = Integer.valueOf(321);
        Integer localInteger6 = Integer.valueOf(321);
        Long localLong = Long.valueOf(3L);

        // 緩存池
        System.out.println(localInteger3 == localInteger4);
        
        // 超出緩存池範圍
        System.out.println(localInteger5 == localInteger6);
        
        // 存在a+b數值表達式,比較的是數值
        System.out.println(localInteger3.intValue() == localInteger1.intValue() + localInteger2.intValue());

        // equals比較的是數值
        System.out.println(localInteger3.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue())));
        // 存在a+b數值表達式,比較的是數值
        System.out.println(localLong.longValue() == localInteger1.intValue() + localInteger2.intValue());
        // Long的equals()先判斷傳遞進來的是不是Long類型,而a+b自動裝箱的是Integer類型
        System.out.println(localLong.equals(Integer.valueOf(localInteger1.intValue() + localInteger2.intValue())));

        // ... 最後一句在這裡漏掉了,大家應該可以推斷出來
    }
}

我使用的反編譯工具是jd-gui,如果還沒有試過反編譯的同學可以下載來玩玩:

二、Synchronize鎖優化手段有哪些

多線程文章回顧:

之前在寫多線程文章的時候,簡單說了一下synchronized鎖在jdk1.6以後會有各種的優化:適應自旋鎖,鎖消除,鎖粗化,輕量級鎖,偏向鎖。

本以為這些優化是非常難以理解的東西,其實不然~~~簡單瞭解一下還是很好理解的。

2.1適應自旋鎖

鎖競爭是kernal mode下的,會經過user mode(用戶態)到kernal mode(內核態) 的切換,是比較花時間的。

自旋鎖出現的原因是人們發現大多數時候鎖的占用只會持續很短的時間,甚至低於切換到kernal mode所花的時間,所以在進入kernal mode前讓線程等待有限的時間,如果在此時間內能夠獲取到鎖就避免了很多無謂的時間,若不能則再進入kernal mode競爭鎖。

在JDK 1.6中引入了自適應的自旋鎖,說明自旋的時間不固定,要不要自旋變得越來越聰明

自旋鎖在JDK1.4.2中就已經引入,只不過預設是關閉的,可以使用-XX:+UseSpinning參數來開啟,在JDK1.6中就已經改為預設開啟了。

參考資料:

2.2鎖消除

如果JVM明顯檢測到某段代碼是線程安全的(言外之意:無鎖也是安全的),JVM會安全地原有的鎖消除掉!

比如說:


    public void vectorTest(){
        Vector<String> vector = new Vector<String>();
        for(int i = 0 ; i < 10 ; i++){
            vector.add(i + "");
        }

        System.out.println(vector);
    }

Vector是預設加鎖的,但JVM如果發現vector變數僅僅在vectorTest()方法中使用,那該vector是線程安全的。JVM會把vector內部加的鎖去除,這個優化就叫做:鎖消除。

2.3鎖粗化

預設情況下,總是推薦將同步塊的作用範圍限制得儘量小

但是如果一系列的連續操作都對同一個對象反覆加鎖和解鎖,甚至加鎖操作是出現在迴圈體中的,頻繁地進行互斥同步操作也會導致不必要的性能損耗

JVM會將加鎖的範圍擴展(粗化),這就叫做鎖粗化。

2.4輕量級鎖

輕量級鎖能提升程式同步性能的依據是“對於絕大部分的鎖,在整個同步周期內都是不存在競爭的”,這是一個經驗數據。

  • 如果沒有競爭,輕量級鎖使用CAS操作避免了使用互斥量的開銷
  • 但如果存在鎖競爭,除了互斥量的開銷外,還額外發生了CAS操作,因此在有競爭的情況下,輕量級鎖會比傳統的重量級鎖更慢。

簡單來說:如果發現同步周期內都是不存在競爭,JVM會使用CAS操作來替代操作系統互斥量。這個優化就被叫做輕量級鎖。

2.5偏向鎖

偏向鎖就是在無競爭的情況下把整個同步都消除掉,連CAS操作都不做了

偏向鎖可以提高帶有同步但無競爭的程式性能。它同樣是一個帶有效益權衡(Trade Off)性質的優化,也就是說,它並不一定總是對程式運行有利,如果程式中大多數的鎖總是被多個不同的線程訪問,那偏向模式就是多餘的。在具體問題具體分析的前提下,有時候使用參數-XX:-UseBiasedLocking來禁止偏向鎖優化反而可以提升性能。

2.6簡單總結各種鎖優化

  • 自適應偏向鎖:自旋時間不固定
  • 鎖消除:如果發現代碼是線程安全的,將鎖去掉
  • 鎖粗化:加鎖範圍過小(重覆加鎖),將加鎖的範圍擴展
  • 輕量級鎖:在無競爭的情況下使用CAS操作去消除同步使用的互斥量
  • 偏向鎖:在無競爭環境下,把整個同步都消除,CAS也不做。

參考資料:

三、TCP粘包,拆包

這是在看wangjingxin大佬面經的時候看到的面試題,之前對TCP粘包,拆包沒什麼概念,於是就簡單去瞭解一下。

3.1什麼是拆包粘包?為什麼會出現?

在進行Java NIO學習時,可能會發現:如果客戶端連續不斷的向服務端發送數據包時,服務端接收的數據會出現兩個數據包粘在一起的情況。

TCP的首部格式:

  • TCP是基於位元組流的,雖然應用層和TCP傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊僅僅看成一連串無結構的位元組流,沒有邊界
  • 從TCP的幀結構也可以看出,在TCP的首部沒有表示數據長度的欄位

基於上面兩點,在使用TCP傳輸數據時,才有粘包或者拆包現象發生的可能。

一個數據包中包含了發送端發送的兩個數據包的信息,這種現象即為粘包

接收端收到了兩個數據包,但是這兩個數據包要麼是不完整的,要麼就是多出來一塊,這種情況即發生了拆包和粘包

拆包和粘包的問題導致接收端在處理的時候會非常困難(因為無法區分一個完整的數據包)

3.2解決拆包和粘包

分包機制一般有兩個通用的解決方法:

  • 1,特殊字元控制
  • 2,在包頭首都添加數據包的長度

如果使用netty的話,就有專門的編碼器和解碼器解決拆包和粘包問題了。

tips:UDP沒有粘包問題,但是有丟包和亂序。不完整的包是不會有的,收到的都是完全正確的包。傳送的數據單位協議是UDP報文或用戶數據報,發送的時候既不合併,也不拆分。

參考資料

四、select、poll、epoll簡單區別

NIO回顧:

在Linux下它是這樣子實現I/O復用模型的:

調用select/poll/epoll其中一個函數,傳入多個文件描述符,如果有一個文件描述符就緒,則返回,否則阻塞直到超時。

這幾個函數是有些區別的,可能有的面試官會問到這三個函數究竟有什麼區別:

區別如下圖:

兩句話總結:

  • select和poll都需要輪詢每個文件描述符,epoll基於事件驅動,不用輪詢
  • select和poll每次都需要拷貝文件描述符,epoll不用
  • select最大連接數受限,epoll和poll最大連接數不受限

tips:epoll在內核中的實現,用紅黑樹管理事件塊

4.1通俗例子

現在3y在公司裡邊實習,寫完的代碼需要給測試測一遍。

select/poll情況:

  • 開發在寫代碼,此時測試挨個問所有開發者,你寫好程式了沒有?要測試嗎?

epoll情況:

  • 開發寫完代碼了,告訴測試:“我寫好代碼了,你去測測,功能是XXX”。於是測試高高興興去找bug了。

其他通俗描述[1]:

一個酒吧服務員(一個線程),前面趴了一群醉漢,突然一個吼一聲“倒酒”(事件),你小跑過去給他倒一杯,然後隨他去吧,突然又一個要倒酒,你又過去倒上,就這樣一個服務員服務好多人,有時沒人喝酒,服務員處於空閑狀態,可以乾點別的玩玩手機。至於epoll與select,poll的區別在於後兩者的場景中醉漢不說話,你要挨個問要不要酒,沒時間玩手機了。io多路復用大概就是指這幾個醉漢共用一個服務員。

來源:

其他通俗描述[2]:

簡單舉個例子(可能也不是很形象)select/poll飯店服務員(內核)告訴飯店老闆(用戶程式):”現在有客人結賬“但是這個服務員沒人明確告訴老闆,哪幾桌的客人結帳。老闆得自兒一個一個桌子去問:請問是你要結帳?epoll飯店服務員(內核)告訴飯店老闆(用戶程式):”1,2,5號客人結賬“老闆就可以直接去1,2,5號桌收錢了

來源:

深入瞭解參考資料:

五、Java記憶體模型

JVM博文回顧:

之前在寫JVM的時候,還一度把JVM記憶體結構與Java記憶體模型給搞混了~~~還好有熱心的網友給我指出來。

JVM記憶體結構:

Java記憶體模型:

操作變數時的規則:

  • Java記憶體模型規定了所有的變數都存儲在主記憶體
  • 線程的工作記憶體中保存了被該線程使用到的變數的主記憶體副本拷貝
  • 線程對變數的所有操作(讀取、賦值等)都必須在工作記憶體中進行,而不能直接讀寫主記憶體中的變數

工作記憶體同步回主記憶體實現是通過以下的8種操作來完成:

  • lock(鎖定):作用於主記憶體的變數,把一個變數標識為一條線程獨占狀態。
  • unlock(解鎖):作用於主記憶體變數,把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他線程鎖定。
  • read(讀取):作用於主記憶體變數,把一個變數值從主記憶體傳輸到線程的工作記憶體中,以便隨後的load動作使用
  • load(載入):作用於工作記憶體的變數,它把read操作從主記憶體中得到的變數值放入工作記憶體的變數副本中。
  • use(使用):作用於工作記憶體的變數,把工作記憶體中的一個變數值傳遞給執行引擎,每當虛擬機遇到一個需要使用變數的值的位元組碼指令時將會執行這個操作。
  • assign(賦值):作用於工作記憶體的變數,它把一個從執行引擎接收到的值賦值給工作記憶體的變數,每當虛擬機遇到一個給變數賦值的位元組碼指令時執行這個操作。
  • store(存儲):作用於工作記憶體的變數,把工作記憶體中的一個變數的值傳送到主記憶體中,以便隨後的write的操作。
  • write(寫入):作用於主記憶體的變數,它把store操作從工作記憶體中一個變數的值傳送到主記憶體的變數中。

Java記憶體模型是圍繞著在併發過程中如何處理原子性、可見性和有序性這3個特征來建立的

保證原子性的操作:

  • read、load、assign、use、store和write
  • synchronized鎖

保證有序性(重排序導致無序)的操作:

  • volatile
  • synchronized鎖

保證可見性:

  • volatile
  • synchronized鎖
  • final

在上面也說了,有序性可以通過volatile和synchronized鎖來保證,但我們一般寫程式的時候不會總是關註代碼的有序性的。其實,我們Java內部中有一個原則,叫做先行發生原則(happens-before)

  • “先行發生”(happens-before)原則可以通過:幾條規則一攬子地解決併發環境下兩個操作之間是否可能存在衝突的所有問題
  • 有了這些規則,並且我們的操作是在這些規則定義的範圍之內。我們就可以確保,A操作肯定比B操作先發生(不會出現重排序的問題)

“先行發生”(happens-before)原則有下麵這麼幾條:

  • 程式次序規則(Program Order Rule):在一個線程內,按照程式代碼順序,書寫在前面的操作先行發生於書寫在後面的操作。準確地說,應該是控制流順序而不是程式代碼順序,因為要考慮分支、迴圈等結構。
  • 管程鎖定規則(Monitor Lock Rule):一個unlock操作先行發生於後面對同一個鎖的lock操作。這裡必須強調的是同一個鎖,而“後面”是指時間上的先後順序。
  • volatile變數規則(Volatile Variable Rule):對一個volatile變數的寫操作先行發生於後面對這個變數的讀操作,這裡的“後面”同樣是指時間上的先後順序。線程啟動規則(Thread Start Rule):Thread對象的start()方法先行發生於此線程的每一個動作。
  • 線程終止規則(Thread Termination Rule):線程中的所有操作都先行發生於對此線程的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值等手段檢測到線程已經終止執行。
  • 線程中斷規則(Thread Interruption Rule):對線程interrupt()方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測到是否有中斷發生。
  • 對象終結規則(Finalizer Rule):一個對象的初始化完成(構造函數執行結束)先行發生於它的finalize()方法的開始。
  • 傳遞性(Transitivity):如果操作A先行發生於操作B,操作B先行發生於操作C,那就可以得出操作A先行發生於操作C的結論。

參考資料:

六、最後

本文簡單整理了一下在學習中做的筆記,還有在網上遇到一些比較重要的知識點(面試題)~希望大家看完能有所收益。

參考資料:

  • 《深入理解Java虛擬機——JVM高級特性與最佳實踐(第2版)》

如果大家有更好的理解方式或者文章有錯誤的地方還請大家不吝在評論區留言,大家互相學習交流~~~

如果想看更多的原創技術文章,歡迎大家關註我的微信公眾號:Java3y。Java技術群討論:742919422。公眾號還有海量的視頻資源哦,關註即可免費領取。

可能感興趣的鏈接:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在基礎面前,一切技巧都是浮雲。 題目是這樣的 要求寫出控制台的輸出. 題目涉及的知識點 this的指向 原型機原型鏈 類的繼承 原始類型和引用類型的區別 每一個知識點都可以拿出來做單獨的專題研究。 解題需要的知識點細節 1.構造函數,都有一個 屬性,指向構造函數的原型對象,實例會共用同一個原型對象; ...
  • javascript的this指向的是一個函數運行時動態綁定對象。 this的4種常見的指向: 作為對象的方法調用 函數被作為一個對象調用,所以this的指向了obj對象。 作為普通函數調用 在普通函數中,this指向的全局對象,也就是window對象。 在這個函數中,因為函數作用域的關係。當我們打 ...
  • 計算屬性是個很好玩的東西,在這裡面可以對數據模型進行操作,·也可以使用getter,setter方法。使用的話也是非常的簡潔明瞭 這裡寫個例子 在computed屬性裡面定義一個計算price的方法,然後對data裡面的東西進行操作 下麵看一下運行結果: 然後再看一下如何使用getter、sette ...
  • 陽光燦爛的一天,我背了一個小包,正打算去商場逛街,悠哉悠哉的走在路上。一隻黑色的野貓跑了過去,我捏造了一個泡泡 var bubble={ }去敲打它的尾巴,它回頭看了一眼我,金色的瞳孔里倒映著一串url,我又吹起一個var bubble={"mainurl":"www.treehole.com",' ...
  • 因為我喜歡打 王者榮耀,我就打開了 王者榮耀的標題 系統把 主播房間的 鏈接放在一個li 元素內的,而data-rid 則對應 房間的ID,我們可以根據 房間id,刪除這些li元素,從而過濾我們不喜歡的主播 我們先 創建一個 removeRoom的分支 然後開始寫代碼 應當註意的是擴展 conten ...
  • 問題引出 新產品的體系架構包含多個模塊,模塊集特點是數量多、模塊間交互複雜。那麼統一介面是一個很好的解決方案,為了實現統一介面打算採用微服務的核心思想,設計了採用restful service的數據交互方式技術架構。這裡記錄一下kafka資源訪問的服務化搭建,後續記錄api和實戰。 解決方案 res ...
  • 一、簡介 RESTEasy是JBoss的一個開源項目,提供各種框架幫助你構建RESTful Web Services和RESTful Java應用程式。它是JAX-RS規範的一個完整實現並通過JCP認證。作為一個JBOSS的項目,它當然能和JBOSS應用伺服器很好地集成在一起。但是,它也能在任何運行 ...
  • 一.最常用通過 Arrays.asList(strArray) 方式,將數組轉換List後,不能對List增刪,只能查改,否則拋異常。 關鍵代碼: 例子: 執行報錯: 報錯原因: 上面程式在list.add(“1”)處拋出異常:UnsupportedOperationException。這是因為Ar ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...