Java面試1-基礎篇

基礎篇

基本功

  • 面向對象的特徵

    面向對象的三個基本特徵是:封裝、繼承、多態。
    封裝
    封裝最好理解了。封裝是面向對象的特徵之一,是對象和類概念的主要特性。
    封裝,也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
    繼承
    面向對象編程 (OOP) 語言的一個主要功能就是“繼承”。繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。
    多態
    多態性(polymorphisn)是允許你將父對象設置成爲和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
    實現多態,有二種方式,覆蓋,重載。

  • final, finally, finalize 的區別

    final 用於聲明屬性,方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承.
    finally 是異常處理語句結構的一部分,表示總是執行.
    finalize 是Object類的一個方法,在垃圾收集器執行的時候會調用被回收對象的此方法,可以覆蓋此方法提供垃圾收集時的其他資源回收,例如關閉文件等. JVM不保證此方法總被調用.

  • int 和 Integer 有什麼區別

    鏈接:https://www.nowcoder.com/questionTerminal/aad1b52a4d98454da9d1d66d0c243a49
    來源:牛客網
    int是java提供的8種原始數據類型之一。Java爲每個原始類型提供了封裝類,Integer是java爲int提供的封裝類。
    int的默認值爲0,而Integer的默認值爲null,是引用類型,即Integer可以區分出未賦值和值爲0的區別,int則無法表達出未賦值的情況,
    Java中int和Integer關係是比較微妙的。關係如下:
    1、int是基本的數據類型;
    2、Integer是int的封裝類;
    3、int和Integer都可以表示某一個數值;
    4、int和Integer不能夠互用,因爲他們兩種不同的數據類型;

  • 重載和重寫的區別

    重載 Overload 表示同一個類中可以有多個名稱相同的方法,但這些方法的參數列表各不相同(即參數個數或類型不同)。
    重寫 Override 表示子類中的方法可以與父類中的某個方法的名稱和參數完全相同,通過子類創建的實例對象調用這個方法時,將調用子類中的定義方法,這相當於把父類中定義的那個完全相同的方法給覆蓋了,這也是面向對象編程的多態性的一種表現。子類覆蓋父類的方法時,只能比父類拋出更少的異常,或者是拋出父類拋出的異常的子異常,因爲子類可以解決父類的一些問題,不能比父類有更多的問題。子類方法的訪問權限只能比父類的更大,不能更小。如果父類的方法是private類型,那麼,子類則不存在覆蓋的限制,相當於子類中增加了一個全新的方法。
    作者:天天向上
    鏈接:https://www.zhihu.com/question/35874324/answer/144589616
    來源:知乎
    著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

  • 抽象類和接口有什麼區別

    抽象類和接口的對比

參數抽象類接口
默認的方法實現它可以有默認的方法實現接口完全是抽象的。它根本不存在方法的實現
實現子類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。子類使用關鍵字implements來實現接口。它需要提供接口中所有聲明的方法的實現
構造器抽象類可以有構造器接口不能有構造器
與正常Java類的區別除了你不能實例化抽象類之外,它和普通Java類沒有任何區別接口是完全不同的類型
訪問修飾符抽象方法可以有publicprotecteddefault這些修飾符接口方法默認修飾符是public。你不可以使用其它修飾符。
main方法抽象方法可以有main方法並且我們可以運行它接口沒有main方法,因此我們不能運行它。
多繼承抽象方法可以繼承一個類和實現多個接口接口只可以繼承一個或多個其它接口
速度它比接口速度要快接口是稍微有點慢的,因爲它需要時間去尋找在類中實現的方法。
添加新方法如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼。如果你往接口中添加方法,那麼你必須改變實現該接口的類。
  • 說說反射的用途及實現

    Java反射機制是一個非常強大的功能,在很多的項目比如Spring,Mybatis都都可以看到反射的身影。通過反射機制,我們可以在運行期間獲取對象的類型信息。利用這一點我們可以實現工廠模式和代理模式等設計模式,同時也可以解決java泛型擦除等令人苦惱的問題。
    獲取一個對象對應的反射類,在Java中有三種方法可以獲取一個對象的反射類,
    通過getClass()方法
    通過Class.forName()方法;
    使用類.class
    通過類加載器實現,getClassLoader()

  • 說說自定義註解的場景及實現

    登陸、權限攔截、日誌處理,以及各種Java框架,如Spring,Hibernate,JUnit 提到註解就不能不說反射,Java自定義註解是通過運行時靠反射獲取註解。實際開發中,例如我們要獲取某個方法的調用日誌,可以通過AOP(動態代理機制)給方法添加切面,通過反射來獲取方法包含的註解,如果包含日誌註解,就進行日誌記錄。反射的實現在 Java 應用層面上講,是通過對 Class 對象的操作實現的,Class 對象爲我們提供了一系列方法對類進行操作。在 Jvm 這個角度來說,Class 文件是一組以 8 位字節爲基礎單位的二進制流,各個數據項目按嚴格的順序緊湊的排列在 Class 文件中,裏面包含了類、方法、字段等等相關數據。通過對 Claas 數據流的處理我們即可得到字段、方法等數據。
    作者:LeopPro
    鏈接:https://juejin.im/post/5a9fad016fb9a028b77a5ce9
    來源:掘金
    著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

  • HTTP 請求的 GET 與 POST 方式的區別

      
    1.根據HTTP規範,GET用於信息獲取,而且應該是安全的和冪等的。
    2.根據HTTP規範,POST表示可能修改變服務器上的資源的請求。
    3.首先是"GET方式提交的數據最多隻能是1024字節",因爲GET是通過URL提交數據,那麼GET可提交的數據量就跟URL的長度有直接關係了。而實際上,URL不存在參數上限的問題,HTTP協議規範沒有對URL長度進行限制。這個限制是特定的瀏覽器及服務器對它的限制。IE對URL長度的限制是2083字節(2K+35)。對於其他瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於操作系統的支持。注意這是限制是整個URL長度,而不僅僅是你的參數值數據長度。

  • POST是沒有大小限制的,HTTP協議規範也沒有進行大小限制

  • session 與 cookie 區別

    1、cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。
    2、cookie不是很安全,別人可以分析存放在本地的COOKIE並進行COOKIE欺騙
    考慮到安全應當使用session。
    3、session會在一定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能
    考慮到減輕服務器性能方面,應當使用COOKIE。
    4、單個cookie保存的數據不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。
    5、所以個人建議:
    將登陸信息等重要信息存放爲SESSION
    其他信息如果需要保留,可以放在COOKIE中

  • session 分佈式處理

    1.Session複製
    在支持Session複製的Web服務器上,通過修改Web服務器的配置,可以實現將Session同步到其它Web服務器上,達到每個Web服務器上都保存一致的Session。
    優點:代碼上不需要做支持和修改。
    缺點:需要依賴支持的Web服務器,一旦更換成不支持的Web服務器就不能使用了,在數據量很大的情況下不僅佔用網絡資源,而且會導致延遲。
    適用場景:只適用於Web服務器比較少且Session數據量少的情況。
    可用方案:開源方案tomcat-redis-session-manager,暫不支持Tomcat8。
    2.Session粘滯
    將用戶的每次請求都通過某種方法強制分發到某一個Web服務器上,只要這個Web服務器上存儲了對應Session數據,就可以實現會話跟蹤。
    優點:使用簡單,沒有額外開銷。
    缺點:一旦某個Web服務器重啓或宕機,相對應的Session數據將會丟失,而且需要依賴負載均衡機制。
    適用場景:對穩定性要求不是很高的業務情景。
    3.Session集中管理
    在單獨的服務器或服務器集羣上使用緩存技術,如Redis存儲Session數據,集中管理所有的Session,所有的Web服務器都從這個存儲介質中存取對應的Session,實現Session共享。
    優點:可靠性高,減少Web服務器的資源開銷。
    缺點:實現上有些複雜,配置較多。
    適用場景:Web服務器較多、要求高可用性的情況。
    可用方案:開源方案Spring Session,也可以自己實現,主要是重寫HttpServletRequestWrapper中的getSession方法,博主也動手寫了一個,github搜索joincat用戶,然後自取。
    4.基於Cookie管理
    這種方式每次發起請求的時候都需要將Session數據放到Cookie中傳遞給服務端。
    優點:不需要依賴額外外部存儲,不需要額外配置。
    缺點:不安全,易被盜取或篡改;Cookie數量和長度有限制,需要消耗更多網絡帶寬。
    適用場景:數據不重要、不敏感且數據量小的情況。
    總結
    這四種方式,相對來說,Session集中管理更加可靠,使用也是最多的。
    作者:JavaQ
    鏈接:https://www.jianshu.com/p/3dd4e06bdfa4
    來源:簡書
    著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

  • JDBC 流程

    (1)向DriverManager類註冊驅動數據庫驅動程序
    (2)調用DriverManager.getConnection方法, 通過JDBC URL,用戶名,密碼取得數據庫連接的Connection對象。
    (3)獲取Connection後, 便可以通過createStatement創建Statement用以執行SQL語句。
    (4) 有時候會得到查詢結果,比如select,得到查詢結果,查詢(SELECT)的結果存放於結果集(ResultSet)中。
    (5)關閉數據庫語句,關閉數據庫連接。

  • MVC 設計思想

    MVC是三個單詞的首字母縮寫,它們是Model(模型)、View(視圖)和Controller(控制)。
    這個模式認爲,程序不論簡單或複雜,從結構上看,都可以分成三層。
    1)最上面的一層,是直接面向最終用戶的"視圖層"(View)。它是提供給用戶的操作界面,是程序的外殼。
    2)最底下的一層,是核心的"數據層"(Model),也就是程序需要操作的數據或信息。
    3)中間的一層,就是"控制層"(Controller),它負責根據用戶從"視圖層"輸入的指令,選取"數據層"中的數據,然後對其進行相應的操作,產生最終結果。

  • equals 與 == 的區別

    ==與equals的主要區別是:==常用於比較原生類型,而equals()方法用於檢查對象的相等性。另一個不同的點是:如果==和equals()用於比較對象,當兩個引用地址相同,==返回true。而equals()可以返回true或者false主要取決於重寫實現。最常見的一個例子,字符串的比較,不同情況==和equals()返回不同的結果。
    使用==比較原生類型如:boolean、int、char等等,使用equals()比較對象。
    ==返回true如果兩個引用指向相同的對象,equals()的返回結果依賴於具體業務實現
    字符串的對比使用equals()代替==操作符
    使用==比較原生類型如:boolean、int、char等等,使用equals()比較對象。
    ==返回true如果兩個引用指向相同的對象,equals()的返回結果依賴於具體業務實現
    字符串的對比使用equals()代替==操作符

集合

  • List 和 Set 區別

    1、List,Set都是繼承自Collection接口
    2、List特點:元素有放入順序,元素可重複 ,Set特點:元素無放入順序,元素不可重複(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的)
    3、List接口有三個實現類:LinkedList,ArrayList,Vector ,Set接口有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet

  • List 和 Map 區別

    List特點:元素有放入順序,元素可重複;
    Map特點:元素按鍵值對存儲,無放入順序 ;
    List接口有三個實現類:LinkedList,ArrayList,Vector;
    LinkedList:底層基於鏈表實現,鏈表內存是散亂的,每一個元素存儲本身內存地址的同時還存儲下一個元素的地址。鏈表增刪快,查找慢;
    Map接口有三個實現類:HashMap,HashTable,LinkeHashMap
    Map相當於和Collection一個級別的;Map該集合存儲鍵值對,且要求保持鍵的唯一性;

  • Arraylist 與 LinkedList 區別

    1) 因爲Array是基於索引(index)的數據結構,它使用索引在數組中搜索和讀取數據是很快的。Array獲取數據的時間複雜度是O(1),但是要刪除數據卻是開銷很大的,因爲這需要重排數組中的所有數據。
    2) 相對於ArrayList,LinkedList插入是更快的。因爲LinkedList不像ArrayList一樣,不需要改變數組的大小,也不需要在數組裝滿的時候要將所有的數據重新裝入一個新的數組,這是ArrayList最壞的一種情況,時間複雜度是O(n),而LinkedList中插入或刪除的時間複雜度僅爲O(1)。ArrayList在插入數據時還需要更新索引(除了插入數組的尾部)。
    3) 類似於插入數據,刪除數據時,LinkedList也優於ArrayList。
    4) LinkedList需要更多的內存,因爲ArrayList的每個索引的位置是實際的數據,而LinkedList中的每個節點中存儲的是實際的數據和前後節點的位置。
    5) 你的應用不會隨機訪問數據。因爲如果你需要LinkedList中的第n個元素的時候,你需要從第一個元素順序數到第n個數據,然後讀取數據。
    6) 你的應用更多的插入和刪除元素,更少的讀取數據。因爲插入和刪除元素不涉及重排數據,所以它要比ArrayList要快。

  • ArrayList 與 Vector 區別

    鏈接:https://www.nowcoder.com/questionTerminal/0953369f92054cbfbf1024a1e723e04f
    來源:牛客網
    1) 同步性:Vector是線程安全的,也就是說是同步的 ,而ArrayList 是線程序不安全的,不是同步的 數2。
    2)數據增長:當需要增長時,Vector默認增長爲原來一倍 ,而ArrayList卻是原來的50% ,這樣,ArrayList就有利於節約內存空間。

    如果涉及到堆棧,隊列等操作,應該考慮用Vector,如果需要快速隨機訪問元素,應該使用ArrayList 。
    
  • HashMap 和 Hashtable 的區別

    1)HashMap幾乎可以等價於Hashtable,除了HashMap是非synchronized的,並可以接受null(HashMap可以接受爲null的鍵值(key)和值(value),而Hashtable則不行)。
    2) HashMap是非synchronized,而Hashtable是synchronized,這意味着Hashtable是線程安全的,多個線程可以共享一個Hashtable;而如果沒有正確的同步的話,多個線程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的擴展性更好。
    3) 另一個區別是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以當有其它線程改變了HashMap的結構(增加或者移除元素),將會拋出ConcurrentModificationException,但迭代器本身的remove()方法移除元素則不會拋出ConcurrentModificationException異常。但這並不是一個一定發生的行爲,要看JVM。這條同樣也是Enumeration和Iterator的區別。
    4) 由於Hashtable是線程安全的也是synchronized,所以在單線程環境下它比HashMap要慢。如果你不需要同步,只需要單一線程,那麼使用HashMap性能要好過Hashtable。
    5) HashMap不能保證隨着時間的推移Map中的元素次序是不變的。

  • HashSet 和 HashMap 區別

    HashMap | HashSet |
    | ------------------------------------------- | ------------------------------------------------------------ |
    | HashMap實現了Map接口 | HashSet實現了Set接口 |
    | HashMap儲存鍵值對 | HashSet僅僅存儲對象 |
    | 使用put()方法將元素放入map中 | 使用add()方法將元素放入set中 |
    | HashMap中使用鍵對象來計算hashcode值 | HashSet使用成員對象來計算hashcode值,對於兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那麼返回false |
    | HashMap比較快,因爲是使用唯一的鍵來獲取對象 | HashSet較HashMap來說比較慢 |

  • HashMap 和 ConcurrentHashMap 的區別

    1)放入HashMap的元素是key-value對。
    (2)底層說白了就是以前數據結構課程講過的散列結構。
    (3)要將元素放入到hashmap中,那麼key的類型必須要實現實現hashcode方法,默認這個方法是根據對象的地址來計算的,具體我也記不太清楚了,接着還必須覆蓋對象的equal方法。
    (4)ConcurrentHashMap對整個桶數組進行了分段,而HashMap則沒有
    (5)ConcurrentHashMap在每一個分段上都用鎖進行保護,從而讓鎖的粒度更精細一些,併發性能更好,而HashMap沒有鎖機制,不是線程安全的。。。

  • HashMap 的工作原理及代碼實現

    HashMap基於hashing原理,我們通過put()和get()方法儲存和獲取對象。當我們將鍵值對傳遞給put()方法時,它調用鍵對象的hashCode()方法來計算hashcode,讓後找到bucket位置來儲存值對象。當獲取對象時,通過鍵對象的equals()方法找到正確的鍵值對,然後返回值對象。HashMap使用鏈表來解決碰撞問題,當發生碰撞了,對象將會儲存在鏈表的下一個節點中。 HashMap在每個鏈表節點中儲存鍵值對對象。

  • ConcurrentHashMap 的工作原理及代碼實現

    ConcurrentHashMap採用了非常精妙的"分段鎖"策略,ConcurrentHashMap的主幹是個Segment數組。Segment繼承了ReentrantLock,所以它就是一種可重入鎖(ReentrantLock)。在ConcurrentHashMap,一個Segment就是一個子哈希表,Segment裏維護了一個HashEntry數組,併發環境下,對於不同Segment的數據進行操作是不用考慮鎖競爭的。

線程

  • 創建線程的方式及實現

    1. 繼承Thread類創建線程類
      (1)定義Thread類的子類,並重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱爲執行體。
      (2)創建Thread子類的實例,即創建了線程對象。
      (3)調用線程對象的start()方法來啓動該線程。
    2. 通過Runnable接口創建線程類
      (1)定義runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
      (2)創建 Runnable實現類的實例,並依此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象。
      (3)調用線程對象的start()方法來啓動該線程。
    3. 通過Callable和Future創建線程
      (1)創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,並且有返回值。
      (2)創建Callable實現類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
      (3)使用FutureTask對象作爲Thread對象的target創建並啓動新線程。
      (4)調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值
      採用實現Runnable、Callable接口的方式創見多線程時,優勢是:
      線程類只是實現了Runnable接口或Callable接口,還可以繼承其他類。
      在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU、代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。
      劣勢是:
      編程稍微複雜,如果要訪問當前線程,則必須使用Thread.currentThread()方法。
      使用繼承Thread類的方式創建多線程時優勢是:
      編寫簡單,如果需要訪問當前線程,則無需使用Thread.currentThread()方法,直接使用this即可獲得當前線程。
      劣勢是:
      線程類已經繼承了Thread類,所以不能再繼承其他父類。
  • sleep() 、join()、yield()有什麼區別

    sleep()
      sleep()方法需要指定等待的時間,它可以讓當前正在執行的線程在指定的時間內暫停執行,進入阻塞狀態,該方法既可以讓其他同優先級或者高優先級的線程得到執行的機會,也可以讓低優先級的線程得到執行機會。但是sleep()方法不會釋放“鎖標誌”,也就是說如果有synchronized同步塊,其他線程仍然不能訪問共享數據。

wait()
  wait()方法需要和notify()及notifyAll()兩個方法一起介紹,這三個方法用於協調多個線程對共享數據的存取,所以必須在synchronized語句塊內使用,也就是說,調用wait(),notify()和notifyAll()的任務在調用這些方法前必須擁有對象的鎖。注意,它們都是Object類的方法,而不是Thread類的方法。
  wait()方法與sleep()方法的不同之處在於,wait()方法會釋放對象的“鎖標誌”。當調用某一對象的wait()方法後,會使當前線程暫停執行,並將當前線程放入對象等待池中,直到調用了notify()方法後,將從對象等待池中移出任意一個線程並放入鎖標誌等待池中,只有鎖標誌等待池中的線程可以獲取鎖標誌,它們隨時準備爭奪鎖的擁有權。當調用了某個對象的notifyAll()方法,會將對象等待池中的所有線程都移動到該對象的鎖標誌等待池。
  除了使用notify()和notifyAll()方法,還可以使用帶毫秒參數的wait(long timeout)方法,效果是在延遲timeout毫秒後,被暫停的線程將被恢復到鎖標誌等待池。
  此外,wait(),notify()及notifyAll()只能在synchronized語句中使用,但是如果使用的是ReenTrantLock實現同步,該如何達到這三個方法的效果呢?解決方法是使用ReenTrantLock.newCondition()獲取一個Condition類對象,然後Condition的await(),signal()以及signalAll()分別對應上面的三個方法。
yield()
  yield()方法和sleep()方法類似,也不會釋放“鎖標誌”,區別在於,它沒有參數,即yield()方法只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行,另外yield()方法只能使同優先級或者高優先級的線程得到執行機會,這也和sleep()方法不同。
join()
  join()方法會使當前線程等待調用join()方法的線程結束後才能繼續執行

  • 說說 CountDownLatch 原理

    CountDownLatch 內部維護了一個整數n,n(要大於等於0)在==當前線程== 初始化CountDownLatch方法指定。當前線程調用 CountDownLatch的await()方法阻塞當前線程,等待其他調用CountDownLatch對象的CountDown()方法的線程執行完畢。 其他線程調用該CountDownLatch的CountDown()方法,該方法會把n-1,直到所有線程執行完成,n等於0,==當前線程==就恢復執行。

  • 說說 CyclicBarrier 原理

    CyclicBarrier簡介CyclicBarrier是一個同步輔助類,允許一組線程互相等待,直到到達某個公共屏障點(commonbarrierpoint)。因爲該barrier在釋放等待線程後可以重用,所以稱它爲循環的barrier。

  • 說說 Semaphore 原理

    Semaphore直譯爲信號。實際上Semaphore可以看做是一個信號的集合。不同的線程能夠從Semaphore中獲取若干個信號量。當Semaphore對象持有的信號量不足時,嘗試從Semaphore中獲取信號的線程將會阻塞。直到其他線程將信號量釋放以後,阻塞的線程會被喚醒,重新嘗試獲取信號量。

  • 說說 Exchanger 原理

    當一個線程到達exchange調用點時,如果它的夥伴線程此前已經調用了此方法,那麼它的夥伴會被調度喚醒並與之進行對象交換,然後各自返回。如果它的夥伴還沒到達交換點,那麼當前線程將會被掛起,直至夥伴線程到達——完成交換正常返回;或者當前線程被中斷——拋出中斷異常;又或者是等候超時——拋出超時異常。

  • 說說 CountDownLatch 與 CyclicBarrier 區別

    (01) CountDownLatch的作用是允許1或N個線程等待其他線程完成執行;而CyclicBarrier則是允許N個線程相互等待。
    (02) CountDownLatch的計數器無法被重置;CyclicBarrier的計數器可以被重置後使用,因此它被稱爲是循環的barrier。

  • ThreadLocal 原理分析

    ThreadLocal提供了線程本地變量,它可以保證訪問到的變量屬於當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同。ThreadLocal相當於提供了一種線程隔離,將變量與線程相綁定。

  • 講講線程池的實現原理

    當提交一個新任務到線程池時,線程池的處理流程如下。
    1)線程池判斷核心線程池裏的線程是否都在執行任務。如果不是,則創建一個新的工作
    線程來執行任務。如果核心線程池裏的線程都在執行任務,則進入下個流程。
    2)線程池判斷工作隊列是否已經滿。如果工作隊列沒有滿,則將新提交的任務存儲在這
    個工作隊列裏。如果工作隊列滿了,則進入下個流程。
    3)線程池判斷線程池的線程是否都處於工作狀態。如果沒有,則創建一個新的工作線程
    來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。

  • 線程池的幾種方式

    在Executors類裏面提供了一些靜態工廠,生成一些常用的線程池。
    1、newFixedThreadPool:創建固定大小的線程池。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。
    2、newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
    3、newSingleThreadExecutor:創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因爲異常結束,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。
    4、newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。
    5、newSingleThreadScheduledExecutor:創建一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。

  • 線程的生命週期

    新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態

鎖機制

  • 說說線程安全問題

    線程安全是多線程領域的問題,線程安全可以簡單理解爲一個方法或者一個實例可以在多線程環境中使用而不會出現問題。
    在Java多線程編程當中,提供了多種實現Java線程安全的方式:

    • 最簡單的方式,使用Synchronization關鍵字:Java Synchronization介紹
    • 使用java.util.concurrent.atomic 包中的原子類,例如 AtomicInteger
    • 使用java.util.concurrent.locks 包中的鎖
    • 使用線程安全的集合ConcurrentHashMap
    • 使用volatile關鍵字,保證變量可見性(直接從內存讀,而不是從線程cache讀)
  • volatile 實現原理

    • 在JVM底層volatile是採用“內存屏障”來實現的。
    • 緩存一致性協議(MESI協議)它確保每個緩存中使用的共享變量的副本是一致的。其核心思想如下:當某個CPU在寫數據時,如果發現操作的變量是共享變量,則會通知其他CPU告知該變量的緩存行是無效的,因此其他CPU在讀取該變量時,發現其無效會重新從主存中加載數據。
  • synchronize 實現原理

    同步代碼塊是使用monitorenter和monitorexit指令實現的,同步方法(在這看不出來需要看JVM底層實現)依靠的是方法修飾符上的ACC_SYNCHRONIZED實現。

  • synchronized 與 lock 的區別

    一、synchronized和lock的用法區別
    (1)synchronized(隱式鎖):在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。
    (2)lock(顯示鎖):需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做爲鎖,多個線程中必須要使用一個ReentrantLock類做爲對 象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。
    二、synchronized和lock性能區別
    synchronized是託管給JVM執行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因爲 這是一個重量級操作,需要調用操作接口,導致有可能加鎖消耗的系統時間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但 是到了Java1.6,發生了變化。synchronize在語義上很清晰,可以進行很多優化,有適應自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導致 在Java1.6上synchronize的性能並不比Lock差。
    三、synchronized和lock機制區別
    (1)synchronized原始採用的是CPU悲觀鎖機制,即線程獲得的是獨佔鎖。獨佔鎖意味着其 他線程只能依靠阻塞來等待線程釋放鎖。
    (2)Lock用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設沒有衝突而去完成某項操作,如果因爲衝突失敗就重試,直到成功爲止。樂觀鎖實現的機制就 是CAS操作(Compare and Swap)。

  • CAS 樂觀鎖

    CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。
    CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前值。)CAS 有效地說明了“我認爲位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”這其實和樂觀鎖的衝突檢查+數據更新的原理是一樣的。

  • ABA 問題

    CAS會導致“ABA問題”。
    CAS算法實現一個重要前提需要取出內存中某時刻的數據,而在下時刻比較並替換,那麼在這個時間差類會導致數據的變化。
    比如說一個線程one從內存位置V中取出A,這時候另一個線程two也從內存中取出A,並且two進行了一些操作變成了B,然後two又將V位置的數據變成A,這時候線程one進行CAS操作發現內存中仍然是A,然後one操作成功。儘管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。
    部分樂觀鎖的實現是通過版本號(version)的方式來解決ABA問題,樂觀鎖每次在執行數據的修改操作時,都會帶上一個版本號,一旦版本號和數據的版本號一致就可以執行修改操作並對版本號執行+1操作,否則就執行失敗。因爲每次操作的版本號都會隨之增加,所以不會出現ABA問題,因爲版本號只會增加不會減少。

  • 樂觀鎖的業務場景及實現方式

    樂觀鎖(Optimistic Lock):
    每次獲取數據的時候,都不會擔心數據被修改,所以每次獲取數據的時候都不會進行加鎖,但是在更新數據的時候需要判斷該數據是否被別人修改過。如果數據被其他線程修改,則不進行數據更新,如果數據沒有被其他線程修改,則進行數據更新。由於數據沒有進行加鎖,期間該數據可以被其他線程進行讀寫操作。
    比較適合讀取操作比較頻繁的場景,如果出現大量的寫入操作,數據發生衝突的可能性就會增大,爲了保證數據的一致性,應用層需要不斷的重新獲取數據,這樣會增加大量的查詢操作,降低了系統的吞吐量。                                                    

【轉載自:】http://www.spring4all.com/article/917

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章