小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Java基礎(chǔ)知識總結(jié)(異常機(jī)制、集合、JUC、IO)

 123xyz123 2021-05-22

1、Java異常以及常用工具類體系。異常處理機(jī)制主要回答了三個問題?

  答:1)、第一個是異常類型回答了什么被拋出。

    2)、第二個是異常堆棧跟蹤回答了在哪里拋出。

    3)、第三個是異常信息回答了為什么被拋出。Throwable是所有異常體系的頂級父類,包含了Error類和Exception類。從概念角度分析Java的異常處理機(jī)制。

2、Java的異常體系,Error和Exception的區(qū)別?

  答:1)、Error,程序無法處理的系統(tǒng)錯誤,編譯器不做檢查。表示系統(tǒng)致命的錯誤,程序無法處理這些錯誤,Error類一般是指與JVM相關(guān)的問題,如果系統(tǒng)奔潰、虛擬機(jī)錯誤、內(nèi)存空間不足、方法調(diào)用棧溢出等等錯誤。
    2)、Exception,程序可以處理的異常,捕獲后可能恢復(fù)。遇到此類異常,盡可能去處理,使程序恢復(fù)運(yùn)行,而不應(yīng)該隨意中止異常。
    3)、總結(jié),Error是程序無法處理的錯誤,Exception是可以處理的異常。

3、Exception主要包含兩類,一類是RuntimeException、另一類是非RuntimeException。

  答:1)、RuntimeException(運(yùn)行時異常)異常表示不可預(yù)知的,程序應(yīng)當(dāng)自行避免,例如數(shù)組下標(biāo)越界,訪問空指針異常等等。
    2)、非RuntimeException(非運(yùn)行時異常)異常是可以預(yù)知的,從編譯器校驗(yàn)的異常。從編譯器角度來說是必須處理的異常,如果不處理此類異常,編譯不能夠通過的。

4、Java的異常體系,從責(zé)任角度來看。

  答:Error屬于JVM需要負(fù)擔(dān)的責(zé)任。RuntimeException是程序應(yīng)該負(fù)擔(dān)的責(zé)任。Checked Exception可檢查異常是Java編譯器應(yīng)該負(fù)擔(dān)的責(zé)任。

5、常見Error以及Exception。RuntimeException運(yùn)行時異常。

  答:1)、第一種,NullPointerException空指針引用異常。
    2)、第二種,ClassCastException類型強(qiáng)轉(zhuǎn)轉(zhuǎn)換異常。
    3)、第三種,IllegalArgumentException傳遞非法參數(shù)異常。
    4)、第四種,IndexOutOfBoundsException下標(biāo)越界異常。
    5)、第五種,NumberFormatException數(shù)字格式異常。

6、非RuntimeException非運(yùn)行時異常。

  答:1)、第一種,ClassNotFoundException,找不到指定的class的異常。
    2)、第二種,IOException,IO操作異常。

7、Error錯誤異常。

  答:1)、第一種,NoClassDefFoundError,找不到class定義的異常。造成的原因包含,類依賴的class或者jar包不存在。類文件存在,但是存在不同的域中。大小寫問題,javac編譯的時候無視大小寫的,很有可能編譯出來的class文件就與想要的不一樣。
    2)、第二種,StackOverflowError,深遞歸導(dǎo)致棧被耗盡而拋出的異常。
    3)、第三種,OutOfMemoryError,內(nèi)存溢出異常。

8、Java的異常處理機(jī)制,Exception的處理機(jī)制。

  答:1)、第一步、拋出異常,創(chuàng)建異常對象,交由運(yùn)行時系統(tǒng)處理。當(dāng)一個方法出現(xiàn)錯誤引發(fā)異常的時候,方法創(chuàng)建異常對象,并交付給運(yùn)行時系統(tǒng),系統(tǒng)對象中包含了異常類型,異常出現(xiàn)時的程序狀態(tài)等異常信息,運(yùn)行時系統(tǒng)負(fù)責(zé)尋找處置異常的代碼并執(zhí)行?! ?/span>

    2)、第二步、捕獲異常,尋找合適的異常處理器處理異常,否則終止運(yùn)行。方法拋出異常以后,運(yùn)行時系統(tǒng)將轉(zhuǎn)為尋找合適的異常處理器,即ExceptionHandle。潛在的異常處理是異常發(fā)生時依次存留在調(diào)用棧方法的集合,當(dāng)異常處理器所能處理的異常類型與拋出的異常類型相符的時候,即為合適的異常處理器,運(yùn)行時系統(tǒng)從發(fā)生異常的方法開始依次回查調(diào)用棧中的方法直至找到含有異常處理器的方法并執(zhí)行。當(dāng)運(yùn)行時系統(tǒng)遍歷了調(diào)用棧都沒有找到合適的異常處理器,則運(yùn)行時系統(tǒng)終止,java程序終止。

9、Java異常的處理規(guī)則。

  答:具體明確,拋出的異常應(yīng)能通過異常類名和message準(zhǔn)確說明異常的類型和產(chǎn)生異常的原因。
    提早拋出,應(yīng)盡可能早的發(fā)現(xiàn)并拋出異常,便于精準(zhǔn)定位問題。
    延遲捕獲,異常的捕獲和處理應(yīng)該盡可能延遲,讓掌握更多信息的作用域來處理異常。

10、try-catch的性能問題。Java異常處理消耗性能的地方。

  答:第一點(diǎn)、try-catch塊影響JVM的優(yōu)化。
    第二點(diǎn)、異常對象實(shí)例需要保存??煺盏鹊刃畔ⅲ_銷較大,這是一個相對較重的操作。所以一定要捕獲可能出現(xiàn)異常的代碼,不要使用一個大大的try-ccatch包起來整段代碼,不要使用異??刂拼a的流程,因?yàn)榇诵蔬h(yuǎn)遠(yuǎn)沒有if-else判斷的效率高。

11、集合之List和Set的區(qū)別,如下所示。

11.1、備注:線程安全和線程不安全的集合:

Vector、HashTable、Properties是線程安全的。
ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是線程不安全的集合類型。

1)、注意:為了保證集合線程是安全的,效率就比較低;線程不安全的集合效率相對會高一些。

2)、如果需要保證集合既是安全的而且效率高,可以使用Collections為我們提出了解決方案,把這些集合包裝成線程安全的集合。

3)、Collections的工具類,將自己創(chuàng)建的集合類實(shí)例傳入進(jìn)去,便可以包裝成一個線程安全的集合類實(shí)例。因?yàn)镾ynchronizedMap有一個Object mutex互斥對象成員,對里面的公共方法使用synchronized對mutex進(jìn)行加鎖操作。相比SynchronizedMap使用的synchronized對mutex進(jìn)行加鎖操作,hashtable線程安全的原因,是在公有的方法都加入了synchronized修飾符,此時獲取的是方法調(diào)用者的鎖。SynchronizedMap和hashtable的原理幾乎相同,唯一的區(qū)別就是鎖定的對象不同,因此這兩者在多線程環(huán)境下,由于都是串行執(zhí)行的,效率比較低下,此時可以學(xué)習(xí)ConcurrentHashMap。

4)、無論是Hashtable還是Collections的工具類SynchronizedMap,當(dāng)多線程并發(fā)的情況下,都要競爭同一把鎖,導(dǎo)致效率極其低下,而在jdk1.5之后,為了改進(jìn)hashTable的痛點(diǎn),ConcurrentHashMap應(yīng)運(yùn)而生,

11.2、Tree的核心在于排序,保證元素排序。

答:1)、自然排序,讓對象所屬的類去實(shí)現(xiàn)comparable接口,無參構(gòu)造,基于元素對象自身實(shí)現(xiàn)的comparable接口的自然排序。
  2)、比較器接口comparator,帶參構(gòu)造,更為靈活,不與單元綁定的comparator接口客戶化排序。

自然排序代碼實(shí)現(xiàn),如下所示:

復(fù)制代碼
1 package com.thread; 2 3 import java.util.Set; 4 import java.util.TreeSet; 5 6 /** 7 * 自然排序,實(shí)現(xiàn)了Comparable自然排序的接口,實(shí)現(xiàn)了該接口就要實(shí)現(xiàn)equals和hashcode方法和compareTo方法。 8 * <p> 9 * <p> 10 * <p> 11 * 為了使Customer類在添加了treeSet之后可以正確排序,要求Customer類里面的equals方法和compareTo方法按照相同的規(guī)則 12 * 來比較兩個對象是否相等。 13 */ 14 public class Customer implements Comparable { 15 16 private String name; 17 private int age; 18 19 public Customer(String name, int age) { 20 this.name = name; 21 this.age = age; 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 public int getAge() { 33 return age; 34 } 35 36 public void setAge(int age) { 37 this.age = age; 38 } 39 40 @Override 41 public boolean equals(Object obj) { 42 // 兩個對象相等返回true 43 if (this == obj) { 44 return true; 45 } 46 if (!(obj instanceof Customer)) { 47 return false; 48 } 49 final Customer other = (Customer) obj; 50 51 if (this.name.equals(other.name) && this.age == other.getAge()) { 52 return true; 53 } else { 54 return false; 55 } 56 } 57 58 @Override 59 public int hashCode() { 60 // 重新了equals方法,就要重新hashcode方法 61 int result; 62 result = (name == null) ? 0 : name.hashCode(); 63 result = 29 * result + age; 64 // 如果兩個對象是相等的,那么hashcode返回值必須是相等的 65 return result; 66 } 67 68 @Override 69 public int compareTo(Object o) { 70 // compareTo返回值大于0,說明Customer1大于Customer2,反之Customer1小于Customer2 71 // 如果等于0,Customer1等于Customer2 72 Customer other = (Customer) o; 73 // 先按照name屬性排序 74 if (this.name.compareTo(other.getName()) > 0) { 75 return 1; 76 } 77 if (this.name.compareTo(other.getName()) < 0) { 78 return -1; 79 } 80 81 // 再按照age屬性排序 82 if (this.age > other.getAge()) { 83 return 1; 84 } 85 if (this.age < other.getAge()) { 86 return -1; 87 } 88 return 0; 89 } 90 91 public static void main(String[] args) { 92 Set<Customer> set = new TreeSet<>(); 93 Customer customer1 = new Customer('tom', 16); 94 Customer customer2 = new Customer('tom', 19); 95 Customer customer3 = new Customer('tom', 20); 96 set.add(customer1); 97 set.add(customer2); 98 set.add(customer3); 99 100 // 循環(huán)遍歷 101 for (Customer c : set) { 102 System.out.println(c.getName() + ' , ' + c.getAge()); 103 } 104 } 105 }
復(fù)制代碼

客戶化排序,實(shí)現(xiàn)Comparator,然后實(shí)現(xiàn)compare方法:

復(fù)制代碼
 1 package com.thread;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * 客戶化排序,實(shí)現(xiàn)Comparator,然后實(shí)現(xiàn)compare方法
 7  */
 8 public class CustomerComparator implements Comparator<Customer> {
 9 
10     @Override
11     public int compare(Customer c1, Customer c2) {
12         // 對姓名進(jìn)行排序
13         if (c1.getName().compareTo(c2.getName()) > 0) {
14             return -1;
15         }
16         if (c1.getName().compareTo(c2.getName()) < 0) {
17             return 1;
18         }
19 
20         // 對年齡進(jìn)行排序
21         if (c1.getAge() - c2.getAge() > 0) {
22             return -1;
23         }
24         if (c1.getAge() - c2.getAge() < 0) {
25             return 1;
26         }
27         return 0;
28     }
29 
30     public static void main(String[] args) {
31         // 此時既使用了自然排序,也使用了客戶化排序,
32         // 在客戶化排序和自然排序共存的情況下,最終結(jié)果以客戶化排序優(yōu)先。
33         // 可以查看TreeMap源碼的get(Object key) -> getEntry(key)方法。
34         // 可以看到先使用客戶化排序得到的結(jié)果。
35         Set<Customer> set = new TreeSet<>(new CustomerComparator());
36         Customer customer1 = new Customer('張三三', 16);
37         Customer customer2 = new Customer('李四四', 19);
38         Customer customer3 = new Customer('王五五', 20);
39         set.add(customer1);
40         set.add(customer2);
41         set.add(customer3);
42         Iterator<Customer> iterator = set.iterator();
43         while (iterator.hasNext()) {
44             Customer next = iterator.next();
45             System.out.println(next.getName() + ' ' + next.getAge());
46         }
47     }
48 
49 }
復(fù)制代碼

12、Map集合。

答:Map集合用于保存具有映射關(guān)系的數(shù)據(jù),Map保存的數(shù)據(jù)都是key-value對的形式的,也就是key-value組成的鍵值對形式的,Map里面的key是不可以重復(fù)的,key是用于標(biāo)示集合里面的每項(xiàng)數(shù)據(jù)的,Map里面的value則是可以重復(fù)的。

13、Hashtable、HashMap、ConcurrentHashMap的區(qū)別,如下所示:

答:1)、HashMap,存儲特點(diǎn)是鍵值對映射,在Java8以前,是數(shù)組+鏈表的組成,HashMap結(jié)合了數(shù)組和鏈表的優(yōu)勢進(jìn)行編寫的。數(shù)組的特點(diǎn)是查詢快,增刪慢,而鏈表的特點(diǎn)是查詢慢,增刪快。HashMap是非Synchronized,所以是線程不安全的,但是效率高。HashMap是由數(shù)組和鏈表組成的,HashMap的數(shù)組長度在未賦初始值的時候,默認(rèn)長度是16的,一個長度為16的數(shù)組中,每個元素存儲的就是鏈表的頭節(jié)點(diǎn),通過類似于hash(key.hashCode) % len,哈希函數(shù)取模的操作獲得要添加的元素所要存放的數(shù)組的位置,實(shí)際上,HashMap的哈希算法是通過位運(yùn)算來進(jìn)行的,相對于取模運(yùn)算呢,效率更高。這里面有一個極端的情況,如果添加到哈希表里面的不同的值的鍵位來通過哈希散列運(yùn)算,總是得出相同的值即分配到同一個桶中,這樣會是某個桶中鏈表的長度變得很長,由于鏈表查詢需要從頭部開始遍歷,因此,在最壞的情況下呢,HashMap性能惡化,從O(1)變成了O(n)。

    HashMap,存儲特點(diǎn)是鍵值對映射,在Java8以后,HashMap采用了數(shù)組 + 鏈表 + 紅黑樹的組成。Java8以后使用常量TREEIFY_THRESHOLD來控制是否將鏈表轉(zhuǎn)換為紅黑樹,來存儲數(shù)據(jù),這意味著,即使在最壞的情況下,HashMap的性能從O(n)提高到O(logn)。

    HashMap的成員變量,Node<K,V>[] table可以看作是Node<K,V>這個數(shù)組和鏈表組成的復(fù)合結(jié)構(gòu),數(shù)組被分為一個個的bucket桶,通過hash值決定了鍵值對在這個數(shù)組的尋址,hash值相同的鍵值對則以鏈表的形式來存儲,而鏈表的大小超過TREEIFY_THRESHOLD =8這個值的時候,就會被改造成紅黑樹,而當(dāng)某個桶上面的元素總數(shù)因?yàn)閯h除變得低于閾值UNTREEIFY_THRESHOLD =6之后,紅黑樹又被轉(zhuǎn)換為鏈表,以保證更高的性能。

  2)、Hashtable是線程安全的,是因?yàn)樵诜椒ǘ技恿藄ynchronized關(guān)鍵字,和Collections.synchronizedMap(map)效果一樣,都是串行執(zhí)行的,效率比較低,唯一的區(qū)別就是鎖定的對象不同而已。為了提升多線程下的執(zhí)行性能,引入了ConcurrentHashMap。
  3)、ConcurrentHashMap,無論是Hashtable還是使用synchronizedMap包裝了的hashMap,當(dāng)多線程并發(fā)的情況下,都要競爭同一把鎖,導(dǎo)致效率極其低下,而在jdk1.5以后,為了改進(jìn)HashTable的缺點(diǎn),引入了ConcurrentHashMap。

  4)、如何優(yōu)化Hashtable呢?如何設(shè)計(jì)ConcurrentHashMap呢?

    a)、通過鎖細(xì)粒度化,將整鎖拆解成多個鎖進(jìn)行優(yōu)化。對象鎖之間是不相互制約的,因此,我們可以將原本一個鎖的行為拆分多個鎖,早期的ConcurrentHashMap也是這樣做的,ConcurrentHashMap早期使用的是分段鎖技術(shù)(由數(shù)組和鏈表組成),通過分段鎖Segment來實(shí)現(xiàn),將鎖一段一段的進(jìn)行存儲,然后給每一段數(shù)據(jù)配一把鎖即Segment,當(dāng)一個線程占用一把鎖即Segment的時候,然后訪問其中一段數(shù)據(jù)的時候呢,位于其他Segment的數(shù)據(jù)也能被其他線程同時訪問,默認(rèn)是分配16個Segment,理論上比Hashtable效率提升了16倍,相比于早期的HashMap,就是將hashMap的table數(shù)組邏輯上拆分成多個子數(shù)組,每個子數(shù)組配置一把鎖,線程在獲取到某把分段鎖的時候,比如,獲取到編號為8的Segment之后呢,才能操作這個子數(shù)組,而其他線程想要操作該子數(shù)組的時候,只能被阻塞,但是如果其他線程操作的是其他未被占用的Segment所管轄的子數(shù)組,那么是不會被阻塞的。此時呢,可以將分段鎖拆分的更細(xì),或者不使用分段鎖,而是table里面的每個bucket都用一把不同的鎖進(jìn)行管理,ConcurrentHashMap的效率就得到了更好的提高。
    b)、jdk1.8以后,當(dāng)前的ConcurrentHashMap,使用的CAS + synchronized使鎖更加細(xì)化,保證并發(fā)安全。同時,也做了進(jìn)一步的優(yōu)化,使用了數(shù)組 + 鏈表 + 紅黑樹的組合。synchronized只鎖定當(dāng)前鏈表或者紅黑樹的首節(jié)點(diǎn),這樣,只要哈希不沖突,就不會產(chǎn)生并發(fā),效率得到了進(jìn)一步的提高,ConcurrentHashMap的結(jié)構(gòu)參考了jdk1.8以后的hashMap來設(shè)計(jì)的。

  5)、Hashtable、HashMap、ConcurrentHashMap的區(qū)別,面試回答:

    a)、HashMap線程不安全的,底層是通過數(shù)組 + 鏈表 + 紅黑樹。鍵值對key-value均可以為null,但是hashtable,ConcurrentHashMap兩個類都不支持。
    b)、Hashtable是線程安全的,鎖住整個對象,底層是數(shù)組 + 鏈表。實(shí)現(xiàn)線程安全的方式,是在修改數(shù)組的時候鎖住整個hashtable,效率很低下的。
    c)、ConcurrentHashMap是線程安全的,CAS + 同步鎖,底層是數(shù)組 + 鏈表 + 紅黑樹。則是對hashtable進(jìn)行了優(yōu)化,通過將鎖細(xì)粒度化到table的每個元素來提升并發(fā)性能。

    d)、HashMap的key、value均可以為null,而其它的兩個類Hashtable、ConcurrentHashMap不支持的。

14、HashMap中的put方法的邏輯,如下所示:

  1)、如果HashMap未被初始化過,則進(jìn)行初始化操作。
  2)、對Key求Hash值,然后再計(jì)算table數(shù)組的下標(biāo)。
  3)、如果沒有碰撞,table數(shù)組里面對應(yīng)的位置還沒有鍵值對,則將鍵值對直接放入對應(yīng)的table數(shù)組位置(桶)中。
  4)、如果碰撞了,table數(shù)組這個位置有元素了,以鏈表的方式鏈接到后面。
  5)、如果鏈表長度超過閾值,就把鏈表轉(zhuǎn)成紅黑樹。
  6)、如果鏈表長度低于6,就把紅黑樹轉(zhuǎn)回鏈表。
  7)、如果節(jié)點(diǎn)已經(jīng)存在就鍵位對應(yīng)的舊值進(jìn)行替換。所謂的節(jié)點(diǎn)存在也就是,即key值已經(jīng)存在在了HashMap中了,我們找到這個key值就key對應(yīng)的新值替換掉它對應(yīng)的舊值。
  8)、如果桶滿了(容量16*加載因子0.75),需要擴(kuò)容了,就需要resize(擴(kuò)容2倍后重排)。

復(fù)制代碼
1 /** 2 * The default initial capacity - MUST be a power of two. 3 */ 4 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 5 6 /** 7 * The maximum capacity, used if a higher value is implicitly specified 8 * by either of the constructors with arguments. 9 * MUST be a power of two <= 1<<30. 10 */ 11 // 12 static final int MAXIMUM_CAPACITY = 1 << 30; 13 14 /** 15 * The load factor used when none specified in constructor. 16 */ 17 // 默認(rèn)的擴(kuò)容因子DEFAULT_LOAD_FACTOR = 0.75f 18 static final float DEFAULT_LOAD_FACTOR = 0.75f; 19 20 /** 21 * The bin count threshold for using a tree rather than list for a 22 * bin. Bins are converted to trees when adding an element to a 23 * bin with at least this many nodes. The value must be greater 24 * than 2 and should be at least 8 to mesh with assumptions in 25 * tree removal about conversion back to plain bins upon 26 * shrinkage. 27 */ 28 // 如果鏈表的長度超過TREEIFY_THRESHOLD = 8的時候呢,就會被改造成紅黑樹。 29 static final int TREEIFY_THRESHOLD = 8; 30 31 /** 32 * The bin count threshold for untreeifying a (split) bin during a 33 * resize operation. Should be less than TREEIFY_THRESHOLD, and at 34 * most 6 to mesh with shrinkage detection under removal. 35 */ 36 // 如果某個桶上面元素的總數(shù),因?yàn)閯h除而低于閾值之后呢,即低于UNTREEIFY_THRESHOLD = 6的時候,紅黑樹又被轉(zhuǎn)換成了鏈表以保證更高的性能。 37 static final int UNTREEIFY_THRESHOLD = 6; 38 39 /** 40 * The smallest table capacity for which bins may be treeified. 41 * (Otherwise the table is resized if too many nodes in a bin.) 42 * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts 43 * between resizing and treeification thresholds. 44 */ 45 // 46 static final int MIN_TREEIFY_CAPACITY = 64; 47 48 49 50 /** 51 * Constructs an empty <tt>HashMap</tt> with the default initial capacity 52 * (16) and the default load factor (0.75). 53 */ 54 // 無參構(gòu)造函數(shù),table的數(shù)組并沒有在構(gòu)造函數(shù)中進(jìn)行初始化,而是僅僅給了一些成員變量賦初始值。HashMap是按照LazyLoad的原則,在首次使用的時候才會被初始化的。 55 public HashMap() { 56 this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted 57 } 58 59 60 61 62 ...... 63 64 65 66 67 68 /** 69 * Basic hash bin node, used for most entries. (See below for 70 * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) 71 */ 72 static class Node<K,V> implements Map.Entry<K,V> { 73 final int hash; // hash值 74 final K key; //鍵值key 75 V value; //鍵值value 76 Node<K,V> next; //指向下一個節(jié)點(diǎn)的next 77 78 Node(int hash, K key, V value, Node<K,V> next) { 79 this.hash = hash; 80 this.key = key; 81 this.value = value; 82 this.next = next; 83 } 84 85 public final K getKey() { return key; } 86 public final V getValue() { return value; } 87 public final String toString() { return key + '=' + value; } 88 89 public final int hashCode() { 90 return Objects.hashCode(key) ^ Objects.hashCode(value); 91 } 92 93 public final V setValue(V newValue) { 94 V oldValue = value; 95 value = newValue; 96 return oldValue; 97 } 98 99 public final boolean equals(Object o) { 100 if (o == this) 101 return true; 102 if (o instanceof Map.Entry) { 103 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 104 if (Objects.equals(key, e.getKey()) && 105 Objects.equals(value, e.getValue())) 106 return true; 107 } 108 return false; 109 } 110 } 111 112 113 114 115 116 117 /** 118 * The table, initialized on first use, and resized as 119 * necessary. When allocated, length is always a power of two. 120 * (We also tolerate length zero in some operations to allow 121 * bootstrapping mechanics that are currently not needed.) 122 */ 123 // HashMap的內(nèi)部結(jié)構(gòu),HashMap可以看作是通過數(shù)組Node<K,V>[] table,和鏈表組合而成的復(fù)合結(jié)構(gòu),數(shù)組被分為一個個的bucket,通過hash值決定了鍵值對在這個數(shù)組的尋址,hash值相同的鍵值對則以鏈表的形式來存儲。 124 transient Node<K,V>[] table; 125 126 127 128 129 ...... 130 131 132 133 134 135 /** 136 * Associates the specified value with the specified key in this map. 137 * If the map previously contained a mapping for the key, the old 138 * value is replaced. 139 * 140 * @param key key with which the specified value is to be associated 141 * @param value value to be associated with the specified key 142 * @return the previous value associated with <tt>key</tt>, or 143 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 144 * (A <tt>null</tt> return can also indicate that the map 145 * previously associated <tt>null</tt> with <tt>key</tt>.) 146 */ 147 public V put(K key, V value) { 148 return putVal(hash(key), key, value, false, true); 149 } 150 151 152 /** 153 * Implements Map.put and related methods 154 * 155 * @param hash hash for key 156 * @param key the key 157 * @param value the value to put 158 * @param onlyIfAbsent if true, don't change existing value 159 * @param evict if false, the table is in creation mode. 160 * @return previous value, or null if none 161 */ 162 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 163 boolean evict) { 164 Node<K,V>[] tab; Node<K,V> p; int n, i; 165 // 如果table為null的時候,或者table的長度為0的時候 166 if ((tab = table) == null || (n = tab.length) == 0) 167 // 調(diào)用resize()方法初始化table,resize()方法的作用是進(jìn)行初始化和擴(kuò)容的功能。 168 n = (tab = resize()).length; 169 // 做hash運(yùn)算,算出鍵值對在table里面的具體位置。hash哈希值并不是key本身的hashCode的,而是來自于hash與或產(chǎn)生的結(jié)果的。 170 if ((p = tab[i = (n - 1) & hash]) == null) 171 // 如果通過hash運(yùn)算得到的位置還沒有元素存儲到里面的時候,則會直接new該鍵值對的Node,放到該數(shù)組的位置當(dāng)中tab[i]。 172 tab[i] = newNode(hash, key, value, null); 173 // 否則就繼續(xù)向下走。 174 else { 175 Node<K,V> e; K k; 176 // 如果發(fā)現(xiàn)同樣的位置,已經(jīng)存在鍵值對的時候,且鍵和傳進(jìn)來的鍵是一致的, 177 if (p.hash == hash && 178 ((k = p.key) == key || (key != null && key.equals(k)))) 179 // 則直接替換數(shù)組里面的元素值 180 e = p; 181 // 否則,如果當(dāng)前數(shù)組位置存儲的是否已經(jīng)是樹化后的節(jié)點(diǎn) 182 else if (p instanceof TreeNode) 183 // 如果是樹化了,就按照樹的方式嘗試存儲鍵值對 184 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 185 else { 186 // 如果未樹化,則按照鏈表的插入方式往鏈表后面添加元素 187 for (int binCount = 0; ; ++binCount) { 188 if ((e = p.next) == null) { 189 p.next = newNode(hash, key, value, null); 190 // 判斷鏈表元素的總數(shù),一旦超過了TREEIFY_THRESHOLD,則將鏈表進(jìn)行樹化操作。 191 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 192 // 樹化操作哦 193 treeifyBin(tab, hash); 194 break; 195 } 196 // 197 if (e.hash == hash && 198 ((k = e.key) == key || (key != null && key.equals(k)))) 199 break; 200 p = e; 201 } 202 } 203 // 如果插入的鍵位存在于hashMap中的時候,則對對應(yīng)的鍵位進(jìn)行值的更新操作。 204 if (e != null) { // existing mapping for key 205 V oldValue = e.value; 206 if (!onlyIfAbsent || oldValue == null) 207 e.value = value; 208 afterNodeAccess(e); 209 return oldValue; 210 } 211 } 212 ++modCount; 213 // 當(dāng)hashMap里面的szie大于閾值的時候呢,就對HashMap進(jìn)行擴(kuò)容 214 if (++size > threshold) 215 resize(); 216 afterNodeInsertion(evict); 217 return null; 218 }
復(fù)制代碼

15、HashMap中的get方法的邏輯,如下所示: 

復(fù)制代碼
 1 /**
 2  * Returns the value to which the specified key is mapped,
 3  * or {@code null} if this map contains no mapping for the key.
 4  *
 5  * <p>More formally, if this map contains a mapping from a key
 6  * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
 7  * key.equals(k))}, then this method returns {@code v}; otherwise
 8  * it returns {@code null}.  (There can be at most one such mapping.)
 9  *
10  * <p>A return value of {@code null} does not <i>necessarily</i>
11  * indicate that the map contains no mapping for the key; it's also
12  * possible that the map explicitly maps the key to {@code null}.
13  * The {@link #containsKey containsKey} operation may be used to
14  * distinguish these two cases.
15  *
16  * @see #put(Object, Object)
17  */
18 public V get(Object key) {
19     Node<K,V> e;
20     // 通過傳入的key值進(jìn)行調(diào)用getNode()方法
21     return (e = getNode(hash(key), key)) == null ? null : e.value;
22 }
23 
24 
25 
26 
27 ......
28 
29 
30 
31 
32 
33 /**
34  * Implements Map.get and related methods
35  *
36  * @param hash hash for key
37  * @param key the key
38  * @return the node, or null if none
39  */
40 final Node<K,V> getNode(int hash, Object key) {
41     Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
42     // 鍵對象的hashcode,通過哈希算法,找到bucket的位置
43     if ((tab = table) != null && (n = tab.length) > 0 &&
44         (first = tab[(n - 1) & hash]) != null) {
45         // 找到Bucket的位置以后,調(diào)用key.equals(k))方法找到鏈表中正確的節(jié)點(diǎn),最終找到要找的值
46         if (first.hash == hash && // always check first node
47             ((k = first.key) == key || (key != null && key.equals(k))))
48             return first;
49         if ((e = first.next) != null) {
50             if (first instanceof TreeNode)
51                 return ((TreeNode<K,V>)first).getTreeNode(hash, key);
52             do {
53                 if (e.hash == hash &&
54                     ((k = e.key) == key || (key != null && key.equals(k))))
55                     return e;
56             } while ((e = e.next) != null);
57         }
58     }
59     return null;
60 }
復(fù)制代碼

16、HashMap,如何有效減少碰撞?

  答:樹化這種被動的方式可以提升性能,哈希運(yùn)算也是可以提升性能的關(guān)鍵。

    1)、擾動函數(shù),促使元素位置分布均勻,減少碰撞的機(jī)率。原理就是如果兩個不相等的對象返回不同的hashcode的話,或者說元素位置盡量的分布均勻些,那么碰撞的機(jī)率就會小些,意味著有些元素就可以通過數(shù)組來直接去獲取了,這樣可以提升hashMap的性能的。哈希算法的內(nèi)部實(shí)現(xiàn),是讓不同對象返回不同的hashcode值。
    2)、其次,如果使用final對象,并采用合適的equals()和hashCode()方法,將會減少碰撞的發(fā)生。不可變性使得能夠緩存不同鍵的hashcode,這將提供獲取對象的速度,而使用String,Integer,這種是非常好的選擇,因?yàn)樗麄兪莊inal,并且重寫了hashcode方法和equals方法的。不可變性final是必要的,因?yàn)闉榱艘?jì)算hashcode,就要防止鍵值改變,如果鍵值在放入的時候和獲取的時候返回不同的hashcode的話呢,就不能從hashMap中找到想要的對象了。

復(fù)制代碼
1 /** 2 * Computes key.hashCode() and spreads (XORs) higher bits of hash 3 * to lower. Because the table uses power-of-two masking, sets of 4 * hashes that vary only in bits above the current mask will 5 * always collide. (Among known examples are sets of Float keys 6 * holding consecutive whole numbers in small tables.) So we 7 * apply a transform that spreads the impact of higher bits 8 * downward. There is a tradeoff between speed, utility, and 9 * quality of bit-spreading. Because many common sets of hashes 10 * are already reasonably distributed (so don't benefit from 11 * spreading), and because we use trees to handle large sets of 12 * collisions in bins, we just XOR some shifted bits in the 13 * cheapest possible way to reduce systematic lossage, as well as 14 * to incorporate impact of the highest bits that would otherwise 15 * never be used in index calculations because of table bounds. 16 */ 17 static final int hash(Object key) { 18 int h; 19 // 即先獲取key.hashCode(),hashCode方法返回值是int類型的,是32位的,然后再將高位數(shù)移位到低位,移動16位,最后進(jìn)行異或運(yùn)算。 20 return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 21 }
復(fù)制代碼

16.1、HashMap,從獲取hash到散列的過程。

  1)、不使用hashcode()方法獲取的值,是因?yàn)閗ey.hashCode();方法返回的是int類型的散列值,如果直接使用這個散列值作為下標(biāo)去訪問hashMap數(shù)組的話呢,考慮到二進(jìn)制的32位帶符號的int值的范圍呢-2147483648——2147483647,前后區(qū)間大概有40億的映射空間,只要哈希函數(shù)映射的均勻松散,一般應(yīng)用是很難出現(xiàn)碰撞的,但是40億長度的數(shù)組在內(nèi)存中是放不下的,況且,HashMap在擴(kuò)容之前數(shù)組默認(rèn)大小才是16,所以直接拿這個散列值使用不現(xiàn)實(shí)的。
  2)、h >>> 16,右移16位,再和自己做異或操作。這樣做,就是為了混合原始哈希碼的高位與低位,依次來加大低位的隨機(jī)性,而且混合后的低位參雜了高位部分的特征,這樣高位的信息也變相的保存了下來,這樣做主要從速度,質(zhì)量,功效進(jìn)行考慮的,可以在數(shù)組table的length在比較小的時候,也能保證考慮到高低bit都參與到哈希的運(yùn)算中,同時也不會有太大的開銷。

17、hashMap含參的構(gòu)造器,可以傳入初始化的hashMap的初始化大小的,根據(jù)傳入的初始化值,換算成2的n次方,轉(zhuǎn)換成最接近的2的倍數(shù)的值,這樣做,就是為了通過哈希運(yùn)算定位桶的時候呢,能實(shí)現(xiàn)用與操作來代替取模進(jìn)而獲得更好的效果。

復(fù)制代碼
 1 /**
 2  * Constructs an empty <tt>HashMap</tt> with the specified initial
 3  * capacity and the default load factor (0.75).
 4  *
 5  * @param  initialCapacity the initial capacity.
 6  * @throws IllegalArgumentException if the initial capacity is negative.
 7  */
 8 // hashMap含參的構(gòu)造器,可以傳入初始化的hashMap的初始化大小的
 9 public HashMap(int initialCapacity) {
10     this(initialCapacity, DEFAULT_LOAD_FACTOR);
11 }
12 
13 
14 
15 /**
16  * Constructs an empty <tt>HashMap</tt> with the specified initial
17  * capacity and load factor.
18  *
19  * @param  initialCapacity the initial capacity
20  * @param  loadFactor      the load factor
21  * @throws IllegalArgumentException if the initial capacity is negative
22  *         or the load factor is nonpositive
23  */
24 // hashMap含參的構(gòu)造器,調(diào)用該構(gòu)造器。
25 public HashMap(int initialCapacity, float loadFactor) {
26     if (initialCapacity < 0)
27         throw new IllegalArgumentException('Illegal initial capacity: ' +
28                                            initialCapacity);
29     if (initialCapacity > MAXIMUM_CAPACITY)
30         initialCapacity = MAXIMUM_CAPACITY;
31     if (loadFactor <= 0 || Float.isNaN(loadFactor))
32         throw new IllegalArgumentException('Illegal load factor: ' +
33                                            loadFactor);
34     this.loadFactor = loadFactor;
35     // 根據(jù)傳入的hashMap的初始化值,并不是傳入的初始化值多大,就是多大的
36     this.threshold = tableSizeFor(initialCapacity);
37 }
38 
39 
40 
41 
42 .......
43 
44 
45 
46 
47 /**
48  * Returns a power of two size for the given target capacity.
49  */
50 // 根據(jù)傳入的初始化值,換算成2的n次方,轉(zhuǎn)換成最接近的2的倍數(shù)的值,這樣做,就是為了通過哈希運(yùn)算定位桶的時候呢,能實(shí)現(xiàn)用與操作來代替取模進(jìn)而獲得更好的效果。
51 static final int tableSizeFor(int cap) {
52     int n = cap - 1;
53     n |= n >>> 1;
54     n |= n >>> 2;
55     n |= n >>> 4;
56     n |= n >>> 8;
57     n |= n >>> 16;
58     return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
59 }
復(fù)制代碼

18 、hashMap的擴(kuò)容resize?

  1)、hashMap的擴(kuò)容,就是重新計(jì)算容量,向hashMap對象中不停的添加元素,而hashMap對象內(nèi)部的數(shù)組無法裝載更多的元素的時候,對象就需要擴(kuò)大數(shù)組的長度了,才能裝入更多的元素。java中的數(shù)組是無法進(jìn)行自動擴(kuò)容的,hashMap的擴(kuò)容,是使用新的大的數(shù)組替換小的數(shù)組。

  2)、hashMap的默認(rèn)負(fù)載因子是0.75f,當(dāng)hashMap填滿了75%的bucket的時候呢,就會創(chuàng)建原來hashMap大小2倍的bucket數(shù)組,來重新調(diào)整map的大小,并將原來的對象放入的新的bucket數(shù)組中。

  3)、HashMap擴(kuò)容的問題,多線程環(huán)境下,調(diào)整大小會存在條件競爭,容易造成死鎖。rehashing是一個比較耗時的過程,由于需要將原先的hashMap中的鍵值對重新移動的新的hashMap中去,是一個比較耗時的過程。

復(fù)制代碼
1 /** 2 * The load factor used when none specified in constructor. 3 */ 4 // 默認(rèn)的負(fù)載因子 5 static final float DEFAULT_LOAD_FACTOR = 0.75f; 6 7 8 9 10 ...... 11 12 13 14 15 16 /** 17 * Initializes or doubles table size. If null, allocates in 18 * accord with initial capacity target held in field threshold. 19 * Otherwise, because we are using power-of-two expansion, the 20 * elements from each bin must either stay at same index, or move 21 * with a power of two offset in the new table. 22 * 23 * @return the table 24 */ 25 // hashMap的擴(kuò)容resize,就是重新計(jì)算容量,向hashMap對象中不停的添加元素,而hashMap對象內(nèi)部的數(shù)組無法裝載更多的元素的時候,對象就需要擴(kuò)大數(shù)組的長度了,才能裝入更多的元素。java中的數(shù)組是無法進(jìn)行自動擴(kuò)容的,hashMap的擴(kuò)容,是使用新的大的數(shù)組替換小的數(shù)組。 26 27 // hashMap的默認(rèn)負(fù)載因子是0.75f,當(dāng)hashMap填滿了75%的bucket的時候呢,就會創(chuàng)建原來hashMap大小2倍的bucket數(shù)組,來重新調(diào)整map的大小,并將原來的對象放入的新的bucket數(shù)組中。 28 final Node<K,V>[] resize() { 29 Node<K,V>[] oldTab = table; 30 int oldCap = (oldTab == null) ? 0 : oldTab.length; 31 int oldThr = threshold; 32 int newCap, newThr = 0; 33 if (oldCap > 0) { 34 if (oldCap >= MAXIMUM_CAPACITY) { 35 threshold = Integer.MAX_VALUE; 36 return oldTab; 37 } 38 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 39 oldCap >= DEFAULT_INITIAL_CAPACITY) 40 newThr = oldThr << 1; // double threshold 41 } 42 else if (oldThr > 0) // initial capacity was placed in threshold 43 newCap = oldThr; 44 else { // zero initial threshold signifies using defaults 45 newCap = DEFAULT_INITIAL_CAPACITY; 46 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 47 } 48 if (newThr == 0) { 49 float ft = (float)newCap * loadFactor; 50 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 51 (int)ft : Integer.MAX_VALUE); 52 } 53 threshold = newThr; 54 @SuppressWarnings({'rawtypes','unchecked'}) 55 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 56 table = newTab; 57 if (oldTab != null) { 58 for (int j = 0; j < oldCap; ++j) { 59 Node<K,V> e; 60 if ((e = oldTab[j]) != null) { 61 oldTab[j] = null; 62 if (e.next == null) 63 newTab[e.hash & (newCap - 1)] = e; 64 else if (e instanceof TreeNode) 65 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 66 else { // preserve order 67 Node<K,V> loHead = null, loTail = null; 68 Node<K,V> hiHead = null, hiTail = null; 69 Node<K,V> next; 70 do { 71 next = e.next; 72 if ((e.hash & oldCap) == 0) { 73 if (loTail == null) 74 loHead = e; 75 else 76 loTail.next = e; 77 loTail = e; 78 } 79 else { 80 if (hiTail == null) 81 hiHead = e; 82 else 83 hiTail.next = e; 84 hiTail = e; 85 } 86 } while ((e = next) != null); 87 if (loTail != null) { 88 loTail.next = null; 89 newTab[j] = loHead; 90 } 91 if (hiTail != null) { 92 hiTail.next = null; 93 newTab[j + oldCap] = hiHead; 94 } 95 } 96 } 97 } 98 } 99 return newTab; 100 }
復(fù)制代碼

19、 ConcurrentHashMap是出自于JUC包的,ConcurrentHashMap有很多地方和hashMap類似的,包含屬性參數(shù)之類的。ConcurrentHashMap使用的CAS + synchronized進(jìn)行高效的同步更新數(shù)據(jù)的。

ConcurrentHashMap總結(jié),jdk1.8的實(shí)現(xiàn),也是鎖分離的思想,比起Segment,鎖拆的更細(xì),只要哈希不沖突,就不會出現(xiàn)并發(fā)或者鎖的情況。

  1)、首先使用無鎖操作CAS插入頭節(jié)點(diǎn),失敗則循環(huán)重試,如果插入失敗,則說明有別的線程插入頭節(jié)點(diǎn)了,需要再次循環(huán)進(jìn)行操作。
  2)、若頭節(jié)點(diǎn)已經(jīng)存在,則通過synchronized嘗試獲取頭節(jié)點(diǎn)的同步鎖,再進(jìn)行操作。性能比Segment分段鎖又提高了很多。

20、ConcurrentHashMap的put方法的邏輯。

  1)、判斷Node[]數(shù)組是否初始化,沒有則進(jìn)行初始化操作。
  2)、通過hash定位數(shù)組的索引坐標(biāo),是否有Node節(jié)點(diǎn),如果沒有則使用CAS進(jìn)行添加(鏈表的頭節(jié)點(diǎn)),添加失敗則進(jìn)入下次循環(huán),繼續(xù)嘗試添加。
  3)、檢查到內(nèi)部正在擴(kuò)容,如果正在擴(kuò)容,就調(diào)用helpTransfer方法,就幫助它一塊擴(kuò)容。
  4)、如果f!=null,頭節(jié)點(diǎn)不為空,則使用synchronized鎖住f元素(鏈表/紅黑二叉樹的頭元素)。如果是Node鏈表結(jié)構(gòu),則執(zhí)行鏈表的添加操作。如果是TreeNode(樹形結(jié)構(gòu))則執(zhí)行樹添加操作。
  5)、判斷鏈表長度已經(jīng)到達(dá)臨界值8,當(dāng)然這個8是默認(rèn)值,大家可以去做調(diào)整,當(dāng)節(jié)點(diǎn)數(shù)超過這個值就需要把鏈表轉(zhuǎn)換成樹結(jié)構(gòu)了。

復(fù)制代碼
  1 private static final int MAXIMUM_CAPACITY = 1 << 30;
  2 
  3 /**
  4  * The default initial table capacity.  Must be a power of 2
  5  * (i.e., at least 1) and at most MAXIMUM_CAPACITY.
  6  */
  7 private static final int DEFAULT_CAPACITY = 16;
  8 
  9 /**
 10  * The largest possible (non-power of two) array size.
 11  * Needed by toArray and related methods.
 12  */
 13 static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 14 
 15 /**
 16  * The default concurrency level for this table. Unused but
 17  * defined for compatibility with previous versions of this class.
 18  */
 19 private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
 20 
 21 /**
 22  * The load factor for this table. Overrides of this value in
 23  * constructors affect only the initial table capacity.  The
 24  * actual floating point value isn't normally used -- it is
 25  * simpler to use expressions such as {@code n - (n >>> 2)} for
 26  * the associated resizing threshold.
 27  */
 28 private static final float LOAD_FACTOR = 0.75f;
 29 
 30 /**
 31  * The bin count threshold for using a tree rather than list for a
 32  * bin.  Bins are converted to trees when adding an element to a
 33  * bin with at least this many nodes. The value must be greater
 34  * than 2, and should be at least 8 to mesh with assumptions in
 35  * tree removal about conversion back to plain bins upon
 36  * shrinkage.
 37  */
 38 static final int TREEIFY_THRESHOLD = 8;
 39 
 40 /**
 41  * The bin count threshold for untreeifying a (split) bin during a
 42  * resize operation. Should be less than TREEIFY_THRESHOLD, and at
 43  * most 6 to mesh with shrinkage detection under removal.
 44  */
 45 static final int UNTREEIFY_THRESHOLD = 6;
 46 
 47 /**
 48  * The smallest table capacity for which bins may be treeified.
 49  * (Otherwise the table is resized if too many nodes in a bin.)
 50  * The value should be at least 4 * TREEIFY_THRESHOLD to avoid
 51  * conflicts between resizing and treeification thresholds.
 52  */
 53 static final int MIN_TREEIFY_CAPACITY = 64;
 54 
 55 /**
 56  * Minimum number of rebinnings per transfer step. Ranges are
 57  * subdivided to allow multiple resizer threads.  This value
 58  * serves as a lower bound to avoid resizers encountering
 59  * excessive memory contention.  The value should be at least
 60  * DEFAULT_CAPACITY.
 61  */
 62 private static final int MIN_TRANSFER_STRIDE = 16;
 63 
 64 /**
 65  * The number of bits used for generation stamp in sizeCtl.
 66  * Must be at least 6 for 32bit arrays.
 67  */
 68 private static int RESIZE_STAMP_BITS = 16;
 69 
 70 /**
 71  * The maximum number of threads that can help resize.
 72  * Must fit in 32 - RESIZE_STAMP_BITS bits.
 73  */
 74 private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
 75 
 76 /**
 77  * The bit shift for recording size stamp in sizeCtl.
 78  */
 79 private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
 80 
 81 
 82 
 83 
 84 
 85 
 86 ......
 87 
 88 
 89 
 90 
 91 /*
 92  * Encodings for Node hash fields. See above for explanation.
 93  */
 94 // 其它成員變量主要用來控制線程之間的并發(fā)操作,比如可以同時可以進(jìn)行擴(kuò)容的線程數(shù)等等。 
 95 static final int MOVED     = -1; // hash for forwarding nodes
 96 static final int TREEBIN   = -2; // hash for roots of trees
 97 static final int RESERVED  = -3; // hash for transient reservations
 98 static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
 99 
100 
101 
102 .......
103 
104 
105 
106 
107  /**
108  * Table initialization and resizing control.  When negative, the
109  * table is being initialized or resized: -1 for initialization,
110  * else -(1 + the number of active resizing threads).  Otherwise,
111  * when table is null, holds the initial table size to use upon
112  * creation, or 0 for default. After initialization, holds the
113  * next element count value upon which to resize the table.
114  */
115 // sizeCtl是size control,是做大小控制的標(biāo)識符,是哈希表初始化和擴(kuò)容時候的一個控制位標(biāo)識量,負(fù)數(shù)代表正在進(jìn)行初始化或者擴(kuò)容操作,-1代表正在初始化,-n代表有n-1個線程正在擴(kuò)容操作,正數(shù)或者0代表哈希表還沒有被初始化操作。這個數(shù)值表示初始化或者下一次進(jìn)行擴(kuò)容的大小,因?yàn)橛辛藇olatile修飾符, sizeCtl是多線程之間可見的,對它的改動,其他線程可以立即看得到,確實(shí)可以起到控制的作用的。
116 private transient volatile int sizeCtl;
117 
118 
119 
120 
121 
122 .......
123 
124 
125 
126 
127 
128 
129 /**
130  * Maps the specified key to the specified value in this table.
131  * Neither the key nor the value can be null.
132  *
133  * <p>The value can be retrieved by calling the {@code get} method
134  * with a key that is equal to the original key.
135  *
136  * @param key key with which the specified value is to be associated
137  * @param value value to be associated with the specified key
138  * @return the previous value associated with {@code key}, or
139  *         {@code null} if there was no mapping for {@code key}
140  * @throws NullPointerException if the specified key or value is null
141  */
142 //  ConcurrentHashMap的put方法。
143 public V put(K key, V value) {
144     return putVal(key, value, false);
145 }
146 
147 /** Implementation for put and putIfAbsent */
148 final V putVal(K key, V value, boolean onlyIfAbsent) {
149     // ConcurrentHashMap不允許插入null的鍵值對,即key不能為null或者value不能為null 
150     if (key == null || value == null) throw new NullPointerException();
151     // 計(jì)算key的哈希值
152     int hash = spread(key.hashCode());
153     int binCount = 0;
154     // for循環(huán),因?yàn)槲覀儗?shù)組元素的更新是使用CAS的機(jī)制進(jìn)行更新的,需要不斷的做失敗重試,直到成功為止,因此這里使用了for循環(huán)。
155     for (Node<K,V>[] tab = table;;) {
156         Node<K,V> f; int n, i, fh;
157         // 先判斷數(shù)組是否為空,如果為空或者length等于0
158         if (tab == null || (n = tab.length) == 0)
159             // 就進(jìn)行初始化操作
160             tab = initTable();
161         // 如果不為空,且不等于0,就使用哈希值來找到f,f表示的是鏈表或者紅黑二叉樹的頭節(jié)點(diǎn),即我們數(shù)組里面的元素,根據(jù)哈希值定位到的元素來檢查元素是否存在
162         else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
163             // 如果不存在,嘗試使用CAS進(jìn)行添加,如果添加失敗,則break掉,進(jìn)入下一次循環(huán)
164             if (casTabAt(tab, i, null,
165                          new Node<K,V>(hash, key, value, null)))
166                 break;                   // no lock when adding to empty bin
167         }
168         // 如果我們發(fā)現(xiàn)原先的元素已經(jīng)存在了,此時,由于我們的ConcurrentHashMap是隨時存在于多線程環(huán)境下的,有可能別的線程正在移動它,也就是說,ConcurrentHashMap的內(nèi)部呢,正在移動元素,那么我們就協(xié)助其擴(kuò)容。
169         else if ((fh = f.hash) == MOVED)
170             tab = helpTransfer(tab, f);
171         else {
172             // 這里表示發(fā)生了哈希碰撞
173             V oldVal = null;
174             // 此時,鎖住鏈表或者紅黑二叉樹的頭節(jié)點(diǎn),即我們的數(shù)組元素
175             synchronized (f) {
176                 // 判斷,f是否的鏈表的頭節(jié)點(diǎn)
177                 if (tabAt(tab, i) == f) {
178                     // fh代表的是頭節(jié)點(diǎn)的哈希值
179                     if (fh >= 0) {
180                         // 如果是鏈表的頭節(jié)點(diǎn),就初始化鏈表的計(jì)數(shù)器
181                         binCount = 1;
182                         // 遍歷該鏈表,每遍歷一次,就將計(jì)數(shù)器加一
183                         for (Node<K,V> e = f;; ++binCount) {
184                             K ek;
185                             // 此時,發(fā)現(xiàn),如果節(jié)點(diǎn)存在呢,就去更新對應(yīng)的value值
186                             if (e.hash == hash &&
187                                 ((ek = e.key) == key ||
188                                  (ek != null && key.equals(ek)))) {
189                                 oldVal = e.val;
190                                 if (!onlyIfAbsent)
191                                     e.val = value;
192                                 break;
193                             }
194                             Node<K,V> pred = e;
195                             // 如果不存在,就在鏈表尾部,添加新的節(jié)點(diǎn)
196                             if ((e = e.next) == null) {
197                                 pred.next = new Node<K,V>(hash, key,
198                                                           value, null);
199                                 break;
200                             }
201                         }
202                     }
203                     // 如果頭節(jié)點(diǎn)是紅黑二叉樹的節(jié)點(diǎn)
204                     else if (f instanceof TreeBin) {
205                         Node<K,V> p;
206                         binCount = 2;
207                         // 則嘗試調(diào)用紅黑二叉樹的操作邏輯,去嘗試往樹里面添加節(jié)點(diǎn)
208                         if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
209                                                        value)) != null) {
210                             oldVal = p.val;
211                             if (!onlyIfAbsent)
212                                 p.val = value;
213                         }
214                     }
215                 }
216             }
217             // 如果鏈表長度已經(jīng)已經(jīng)達(dá)到了臨界值8
218             if (binCount != 0) {
219                 if (binCount >= TREEIFY_THRESHOLD)
220                     // 那么,就將鏈表轉(zhuǎn)化為樹結(jié)構(gòu)
221                     treeifyBin(tab, i);
222                 if (oldVal != null)
223                     return oldVal;
224                 break;
225             }
226         }
227     }
228     // 在添加完節(jié)點(diǎn)之后呢,就將當(dāng)前的ConcurrentHashMap的size數(shù)量呢,加上1。
229     addCount(1L, binCount);
230     return null;
231 }
復(fù)制代碼

21、java.util.concurrent,提供了并發(fā)編程的解決方案,java.util.concurrent簡稱為JUC,JUC包里面有兩大核心。

答:1)、CAS是java.util.concurrent.atomic包的基礎(chǔ)。
  2)、AQS是java.util.concurrent.locks包以及一些常用類比如Semophore,ReentrantLock等類的基礎(chǔ)。

22、java.util.concurrent簡稱為JUC,JUC包的分類。

答:1)、線程執(zhí)行器executor,就是任務(wù)的執(zhí)行和調(diào)度的框架,此外,在tools包可以看到和executor相關(guān)的Executors類,用來創(chuàng)建ExecutorService、ScheduledExecutorService、ThreadFactory、Callable對象等等。
  2)、鎖locks。在jdk1.5之前協(xié)調(diào)共享對象的訪問時,可以使用的機(jī)制只有synchronized和volatile,jdk1.5之后便出現(xiàn)了鎖locks,locks里面引入了顯式鎖,方便線程間的共享資源做更細(xì)粒度的鎖控制,Condition對象是由locks鎖對象創(chuàng)建的,一個Lock對象可創(chuàng)建多個Condition對象,主要用于將線程的等待和喚醒,即將wait、notify、notifyAll方法對象化。不管是Condition對象、還是Lock對象都是基于AQS來實(shí)現(xiàn)的,而AQS得底層是通過調(diào)用LockSupport.unpark和LockSupport.park去實(shí)現(xiàn)線程的阻塞和喚醒的。ReentrantReadWriteLock是可重入讀寫鎖,沒有線程進(jìn)行寫操作的時候,多個線程可同時進(jìn)行讀操作,當(dāng)有線程進(jìn)行寫操作的時候,其它讀寫操作只能等待,即讀讀共存,但是讀寫不能共存,寫寫也不能共存,在讀多于寫的情況下,可重入讀寫鎖能夠提供比排它鎖ReentrantLock更好的并發(fā)性和吞吐量。
  3)、原子變量類atomic,中文是原子,指的是一個操作不可中斷的,在多個線程一起執(zhí)行的時候,一個操作一旦開始就不會被其它線程所干擾,所以原子類就是具有原子操作特征的類,atomic包方便程序員在多線程環(huán)境下無鎖的進(jìn)行原子操作。atomic包中一共有12個類,四種原子更新方式,分別是原子更新基本類型,原子更新數(shù)組,原子更新引用,原子更新字段。atomic使用的CAS的更新方式,當(dāng)某個線程在執(zhí)行atomic方法的時候不會被其它線程打斷,而別的線程j就像自旋鎖一樣,一直等到該方法執(zhí)行完成才有JVM從等待隊(duì)列中選擇一個線程來執(zhí)行,在軟件層面上是非阻塞的,是在底層硬件上借助處理器的原子指令來保證的。在面對多線程的累加操作可以適當(dāng)運(yùn)用atomic包里面的類來解決。
  4)、并發(fā)工具類tools。
  5)、并發(fā)集合collections。

23、java.util.concurrent簡稱為JUC,JUC包的并發(fā)工具類。

答:四個同步器,同步器的作用主要是用于協(xié)助線程的同步。

1)、閉鎖CountDownLatch。讓主線程等待一組事件發(fā)生后繼續(xù)執(zhí)行,這里面的事件指的是CountDownLatch里的countDown()方法。

  值的注意的是其它線程調(diào)用完countDown()方法之后還是會繼續(xù)執(zhí)行的,也就是說,countDown()方法調(diào)用之后并不代表該子線程已經(jīng)執(zhí)行完畢,而是告訴主線程說你可以繼續(xù)執(zhí)行,至少我這邊不托你后腿了,具體還需要看其它線程給不給力了,如圖,引入了CountDownLatch之后了,主線程就進(jìn)入了等待狀態(tài),此時CountDownLatch里面有一個cnt變量開始的時候初始化為一個整數(shù),這里就是事件的個數(shù),我們的變量初始化為3,m每當(dāng)其中一個子線程調(diào)用countDown()方法之后,這個計(jì)數(shù)器便會減一,直到所有的子線程都調(diào)用了countDown()方法,cnt變?yōu)榱阒?,主線程才得以重新恢復(fù)到執(zhí)行的狀態(tài)。


2)、柵欄CyclicBarrier,阻塞當(dāng)前線程,等待其它線程。

a)、等待其它線程,且會阻塞自己當(dāng)前線程,所有線程必須同時到達(dá)柵欄位置后,才能繼續(xù)執(zhí)行。
b)、所有線程到達(dá)柵欄處,可以觸發(fā)執(zhí)行另外一個預(yù)先設(shè)置的線程。

  CyclicBarrier和CountDownLatch一樣,內(nèi)部也有一個計(jì)數(shù)器,如圖中的cnt,T1,T2,T3沒調(diào)用一次await()方法,計(jì)數(shù)器就會減一,且在它們調(diào)用await方法的時候,如果計(jì)數(shù)器不為零,這些線程也會被阻塞,另外TA線程j即當(dāng)前線程會在所有線程到達(dá)柵欄處即計(jì)數(shù)器為0的時候才會跟著T1,T2,T3一起去執(zhí)行,同樣都是阻塞當(dāng)前線程來等待其它線程,計(jì)數(shù)的時候CountDownLatch的其它子線程是可以繼續(xù)執(zhí)行的,而CyclicBarrier的所有線程會被阻塞直到計(jì)數(shù)器變?yōu)榱?,這是兩者作用上的區(qū)別。

3)、信號量Semaphore,控制某個資源可被同時訪問的線程個數(shù)。

  通過acquire()方法獲取一個許可,如果沒有就去等待,而一旦利用資源執(zhí)行完業(yè)務(wù)邏輯之后,線程就會調(diào)用release方法去釋放出一個許可出來。

4)、交換器Exchanger,兩個線程到達(dá)同步點(diǎn)后,相互交換數(shù)據(jù)。

  Exchanger提供一個同步點(diǎn),在這個同步點(diǎn),兩個線程k可以交換彼此的數(shù)據(jù),Exchanger會產(chǎn)生一個同步點(diǎn),一個線程先執(zhí)行到達(dá)同步點(diǎn),就會被阻塞,直到另外一個線程也進(jìn)入到同步點(diǎn)為止,當(dāng)兩個都到達(dá)了同步點(diǎn)之后就開始交換數(shù)據(jù),線程中調(diào)用Exchanger.Exchange()的地方就是同步點(diǎn)了,Exchanger只能用于連個線程互相    交換數(shù)據(jù)。

24、BlockingQueue,阻塞隊(duì)列,提供了可阻塞的入隊(duì)和出隊(duì)操作。

答:Collections里面除了ConcurrentHashMap之外,還有BlockingQueue。

  1)、如果隊(duì)列滿了,入隊(duì)操作將阻塞,直到有空間可用,如果隊(duì)列空了,出隊(duì)操作將阻塞,直到有元素可用。根據(jù)出入隊(duì)的規(guī)則和底層數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn),可以劃分出多個BlockingQueue的實(shí)現(xiàn)子類。

  2)、主要用于生產(chǎn)者-消費(fèi)者模式,在多線程場景的時候生產(chǎn)者線程在隊(duì)列尾部添加元素,而消費(fèi)者線程在隊(duì)列頭部消費(fèi)元素,通過這種方式能夠達(dá)到將任務(wù)的生產(chǎn)和消費(fèi)進(jìn)行隔離的目的。


25、BlockingQueue主要有以下的七個隊(duì)列實(shí)現(xiàn),它們都是線程安全的。

  1)、ArrayBlockingQueue,一個由數(shù)據(jù)結(jié)構(gòu)組成的有界阻塞隊(duì)列,有邊界的意思容量是有限的,我們必須在其初始化的時候指定它的容量大小,容量大小一旦指定就不可改變了,以先進(jìn)先出的方式來存儲數(shù)據(jù)的,最新插入的對象是在尾部,最先移除的對象是在頭部。
  2)、LinkedBlockingQueue,一個由鏈表結(jié)構(gòu)組成的有界或者無界阻塞隊(duì)列,阻塞隊(duì)列大小配置是可選的,如果初始化的時候指定大小,那么它就是有邊界的,如果不指定大小,它就是無邊界的,說是無邊界,其實(shí)是采用了默認(rèn)大小的容量,內(nèi)部實(shí)現(xiàn)是一個鏈表,和ArrayBlockingQueue一樣是采用先進(jìn)先出的方式存儲數(shù)據(jù)。
  3)、PriorityBlockingQueue,一個支持優(yōu)先級排序的無界阻塞隊(duì)列,不是先進(jìn)先出的隊(duì)列,元素按照優(yōu)先級順序被移除的,該隊(duì)列沒有上限,但是如果隊(duì)列為空,那么取元素的操作take就會被阻塞,所以它的檢索操作task是受阻的,另外該隊(duì)列的元素是要具備可比性的,這樣才可以按照優(yōu)先級來進(jìn)行操作。
  4)、DealyQueue,一個使用優(yōu)先級隊(duì)列實(shí)現(xiàn)的無界阻塞隊(duì)列,支持延遲獲取元素的無邊界阻塞隊(duì)列,隊(duì)列中的元素必須實(shí)現(xiàn)Delay接口,在創(chuàng)建元素的時候,可以指定多久才能從隊(duì)列中獲取當(dāng)前元素,只有在延遲期滿的時候,才能從隊(duì)列中獲取元素。
  5)、SynchronousQueue,一個不存儲元素的阻塞隊(duì)列,隊(duì)列內(nèi)部僅允許容納一個元素,當(dāng)一個線程插入一個元素后,會被阻塞,直到這個元素被另外一個線程給消費(fèi)掉。
  6)、LinkedTransferQueue,一個由鏈表結(jié)構(gòu)組成的無界阻塞隊(duì)列,是SynchronousQueue和LinkedBlockingQueue合體,性能比LinkedBlockingQueue更高,因?yàn)樗菬o鎖操作,比SynchronousQueue存儲更多的元素。
  7)、LinkedBlockingDeque,一個由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列,是一個雙端隊(duì)列。

26、Java中 BIO、NIO、AIO的主要區(qū)別。

答:1)、BIO是Block-IO,是傳統(tǒng)的java.io以及部分java.net包下的接口或者類,java.net里面比如socket、server socket、http,UrlConnection,因?yàn)榫W(wǎng)絡(luò)通信同樣是IO行為,因此都可以說是輸入BIO的范疇。

  a)、傳統(tǒng)IO基于字節(jié)流和字符流j進(jìn)行操作,提供了InputStream和OutputStream,Reader和Writer。4
  b)、BIO是基于流模型實(shí)現(xiàn)的,這意味著其交互方式是同步阻塞的方式,在讀取輸入流或?qū)懭胼敵隽鞯臅r候,在讀寫操作完成之前,線程會一直阻塞在哪里,它們之間的調(diào)用是可靠的線性順序,程序發(fā)送請求給內(nèi)核,然后有內(nèi)核去進(jìn)行通信,在內(nèi)核準(zhǔn)備好數(shù)據(jù)之前,這個線程是被掛起的,所以在兩個階段程序都處于掛起狀態(tài),類比成Client/Server模式呢,則其實(shí)現(xiàn)模式為一個連接,一個線程即客戶端要連接請求的時候服務(wù)端就需要啟動一個線程進(jìn)行處理,待操作系統(tǒng)返回結(jié)果,如果這個連接不做任何事情,會造成不必要的線程開銷,當(dāng)然可以通過線程池機(jī)制來改善。
  c)、BIO的特點(diǎn)就是在IO執(zhí)行的兩個階段都被阻塞住了,好處就是代碼比較簡單,直觀。缺點(diǎn)就是IO效率和擴(kuò)展性存在瓶頸。

2)、NIO是NonBlock-IO即非阻塞IO,在jdk1.4以后引入了NIO框架,提供了channel、selector、buffer等新的抽象,構(gòu)建多路復(fù)用的,同步非阻塞的IO操作,同時提供了更接近操作系統(tǒng)底層高性能數(shù)據(jù)操作方式。

  a)、NIO與BIO明顯區(qū)別就是,在發(fā)起第一次請求之后,線程并沒有被阻塞,它是反復(fù)去檢查數(shù)據(jù)是否已經(jīng)準(zhǔn)備好,把原來大塊不能用的阻塞的時間分成了許多小阻塞,檢查的是會有一些些阻塞,線程不斷有機(jī)會去被執(zhí)行,檢查這個數(shù)據(jù)有沒有準(zhǔn)備好,有點(diǎn)類似于輪詢,類比成client/server模式呢,其實(shí)現(xiàn)模式為一個請求一個線程,即客戶端發(fā)送的連接請求都會注冊到多路復(fù)用器上,多路復(fù)用器輪循到有IO請求時,才啟動一個線程進(jìn)行處理。NIO的特點(diǎn)就是程序不斷去主動詢問內(nèi)核是否已經(jīng)準(zhǔn)備好,第一個階段是非阻塞的,第二個階段是阻塞的。

27、NIO的核心部分組成,Channels、Buffers、Selectors。

  基本上所有的IO在NIO中都是從一個Channel開始,Channel有點(diǎn)像流,數(shù)據(jù)可以從Channel讀到Buffer中,也可以從Buffer中寫到Channel中。

29、NIO-Channels的類型,涵蓋了TCP和UDP網(wǎng)絡(luò)IO以及文件IO。

  1)、FileChannel,擁有transferTo方法和transferFron方法。transferTo方法把FileChannel中的數(shù)據(jù)拷貝到另外一個Channel。transferFron方法把另外一個Channel中的數(shù)據(jù)拷貝到FileChannel中。該接口常被用于高效的網(wǎng)絡(luò)文件的數(shù)據(jù)傳輸和大文件拷貝,在操作系統(tǒng)支持的情況下,通過該方法傳輸數(shù)據(jù),并不需要將源數(shù)據(jù)從內(nèi)核態(tài)拷貝到用戶態(tài),再從用戶態(tài)拷貝到目標(biāo)通道的內(nèi)核態(tài)。同時也避免了兩次用戶態(tài)和內(nèi)核態(tài)間的上下文切換,即零拷貝,效率較高,其性能高于BIO中提供的方法。
  2)、DatagramChannel。
  3)、SocketChannel。
  4)、ServerSocketChannel。

30、NIO-Buffers的類型,這些Buffer覆蓋了我們能通過IO發(fā)送的基本數(shù)據(jù)類型。

  1)、ByteBuffer。
  2)、CharBuffer。
  3)、DoubleBuffer。
  4)、FloatBuffer。
  5)、IntBuffer。
  6)、LongBuffer。
  7)、ShortBuffer。
  8)、MappedByteBuffer,主要用于表示內(nèi)存映射文件。

31、NIO-Selector。

  Selector允許單線程處理多個Channel,如果你的應(yīng)用打開了多個連接即通道,但每個連接的流量都比較低,使用Selector就會很方便了,例如開發(fā)一個聊天服務(wù)器就排上用場了。如圖所示的是使用一個Selector處理三個Channel的時候,使用Selector得向Selector注冊Channel,然后調(diào)用它的select方法,這個方法會一直阻塞,直到某個注冊的通道有事件就緒,一旦這個方法返回呢,線程就可以處理這些事件了,事件可以是,比如說是有新的連接進(jìn)來,或者說Buffer已經(jīng)有內(nèi)容可以讀取到了等等。

32、NIO的底層使用了操作系統(tǒng)底層的IO多路復(fù)用,調(diào)用系統(tǒng)級別的select、poll、epoll等不同方式,優(yōu)點(diǎn)在于單線程可以同時處理多個網(wǎng)絡(luò)IO,IO多路復(fù)用調(diào)用系統(tǒng)級別的select、poll、epoll模型,由系統(tǒng)監(jiān)控IO狀態(tài),select輪詢可以監(jiān)控許多的IO請求,當(dāng)有一個socket的數(shù)據(jù)被準(zhǔn)備好的時候就可以返回了。

  1)、支持一個進(jìn)程所能打開的最大連接數(shù)。
    a)、select,單個進(jìn)程所能打開的最大連接數(shù)由FD_SETSIZE宏定義,其大小是32個整數(shù)的大?。ㄔ?2位的機(jī)器上,大小是32*32,64的機(jī)器上FD_SETSIZE為32*64),我們可以對其進(jìn)行修改,然后重新編譯內(nèi)核,但是性能無法保證,需要做進(jìn)一步測試。
    b)、poll,本質(zhì)上與select沒有區(qū)別,但是它沒有最大連接數(shù)的限制,原因是它是基于鏈表來存儲的。
    c)、epoll,雖然連接數(shù)有上限,但是很大,1G內(nèi)存的機(jī)器上可以打開10萬左右的連接。
  2)、FD劇增后帶來的IO效率問題。
    a)、select,因?yàn)槊看握{(diào)用時候都會對連接進(jìn)行線性遍歷,所以隨著FD的增加會造成遍歷速度的線性下降的性能問題。
    b)、poll,同上。
    c)、epoll,由于epoll是根據(jù)每個fd上的callback函數(shù)來實(shí)現(xiàn)的,只有活躍的socket才會主動調(diào)用callback,所以在活躍socket較少的情況下,使用epoll不會有線性線性下降的性能問題,但是所有的socket都很活躍的情況下,可能會有性能問題。
  3)、消息傳遞方式。
    a)、select,內(nèi)核需要將消息傳遞到用戶空間,需要內(nèi)核的拷貝動作。
    b)、poll,同上。
  c)、epoll,通過內(nèi)核的用戶空間共享一塊內(nèi)存來實(shí)現(xiàn),性能較高。

33、AIO,Asynchronous IO,基于事件和回調(diào)機(jī)制,異步非阻塞的方式,可以理解為應(yīng)用操作直接返回,而不會阻塞在哪里,當(dāng)后臺處理完成,操作系統(tǒng)就會通知相應(yīng)線程進(jìn)行后續(xù)工作。

AIO屬于異步模型,用戶線程可以同時處理別的事情,AIO如何進(jìn)一步加功處理結(jié)果。Java提供了兩種方法。

  1)、基于回調(diào),實(shí)現(xiàn)CompletionHandler接口,調(diào)用的時候觸發(fā)回調(diào)函數(shù),在調(diào)用的時候,把回調(diào)函數(shù)傳遞給對應(yīng)的API即可。
  2)、返回Future,通過isDone查看是否準(zhǔn)備好,通過get方法等待返回?cái)?shù)據(jù)。

34、BIO、NIO、AIO對比。

屬性\模型 阻塞BIO 非阻塞NIO 異步AIO
blocking 阻塞并同步。 非阻塞但同步 非阻塞并異步
線程數(shù)(server:client) 1:1 1:N 0:N
復(fù)雜度 簡單 較復(fù)雜 復(fù)雜
吞吐量

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多