1. 程式人生 > >深入理解Java中的HashMap的實現原理

深入理解Java中的HashMap的實現原理

HashMap繼承自抽象類AbstractMap,抽象類AbstractMap實現了Map介面。關係圖如下所示:


Java中的Map<key, value>介面允許我們將一個物件作為key,也就是可以用一個物件作為key去查詢另一個物件。
在我們探討HashMap的實現原理之前,我們先自己實現了一個SimpleMap類,該類繼承自AbstractMap類。具體實現如下:
import java.util.*;


public class SimpleMap<K,V> extends AbstractMap<K,V> {
	//keys儲存所有的鍵
	private List<K> keys = new ArrayList<K>();
	//values儲存所有的值
	private List<V> values = new ArrayList<V>();
	
	
	/**
	 * 該方法獲取Map中所有的鍵值對
	 */
	@Override
	public Set entrySet() {
		Set<Map.Entry<K, V>> set = new SimpleSet<Map.Entry<K,V>>();
		
		//keys的size和values的size應該一直是一樣大的
		Iterator<K> keyIterator = keys.iterator();
		Iterator<V> valueIterator = values.iterator();
		while(keyIterator.hasNext() && valueIterator.hasNext()){
			K key = keyIterator.next();
			V value = valueIterator.next();
			SimpleEntry<K,V> entry = new SimpleEntry<K,V>(key, value);
			set.add(entry);
		}
		
		return set;
	}

	@Override
	public V put(K key, V value) {
		V oldValue = null;
		int index = this.keys.indexOf(key);
		if(index >= 0){
			//keys中已經存在鍵key,更新key對應的value
			oldValue = this.values.get(index);
			this.values.set(index, value);
		}else{
			//keys中不存在鍵key,將key和value作為鍵值對新增進去
			this.keys.add(key);
			this.values.add(value);
		}
		return oldValue;
	}
	
	@Override
	public V get(Object key) {
		V value = null;
		int index = this.keys.indexOf(key);
		if(index >= 0){
			value = this.values.get(index);
		}
		return value;
	}

	@Override
	public V remove(Object key) {
		V oldValue = null;
		int index = this.keys.indexOf(key);
		if(index >= 0){
			oldValue = this.values.get(index);
			this.keys.remove(index);
			this.values.remove(index);
		}
		return oldValue;
	}

	@Override
	public void clear() {
		this.keys.clear();
		this.values.clear();
	}
	
	@Override
	public Set keySet() {
		Set<K> set = new SimpleSet<K>();
		Iterator<K> keyIterator = this.keys.iterator();
		while(keyIterator.hasNext()){
			set.add(keyIterator.next());
		}
		return set;
	}

	@Override
	public int size() {
		return this.keys.size();
	}

	@Override
	public boolean containsValue(Object value) {
		return this.values.contains(value);
	}

	@Override
	public boolean containsKey(Object key) {
		return this.keys.contains(key);
	}

	@Override
	public Collection values() {
		return this.values();
	}

}

當子類繼承自AbstractMap類時,我們只需要實現AbstractMap類中的entrySet方法和put方法即可,entrySet方法是用來返回該Map所有鍵值對的一個Set,put方法是實現將一個鍵值對放入到該Map中。大家可以看到,我們上面的程式碼不僅除了實現entrySet和put方法外,我們還重寫了get、remove、clear、keySet、values等諸多方法。其實我們只要重寫entrySet和put方法,該類就可以正確執行,那我們為什麼還要重寫剩餘的那些方法呢?AbstractMap這個方法做了很多處理操作,Map中的很多方法在AbstractMap都實現了,而且很多方法都依賴於entrySet方法,舉個例子,Map介面中的values方法是讓我們返回該Map中所有的值的Collection。我們可以看一下AbstractMap中對values方法的實現:
public Collection<V> values() {
        if (values == null) {
            values = new AbstractCollection<V>() {
                public Iterator<V> iterator() {
                    return new Iterator<V>() {
                        private Iterator<Entry<K,V>> i = entrySet().iterator();

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public V next() {
                            return i.next().getValue();
                        }

                        public void remove() {
                            i.remove();
                        }
                    };
                }

                public int size() {
                    return AbstractMap.this.size();
                }

                public boolean isEmpty() {
                    return AbstractMap.this.isEmpty();
                }

                public void clear() {
                    AbstractMap.this.clear();
                }

                public boolean contains(Object v) {
                    return AbstractMap.this.containsValue(v);
                }
            };
        }
        return values;
    }

大家可以看到,程式碼不少,基本的思路是先通過entrySet生成包含所有鍵值對的Set,然後通過迭代獲取其中的value值。其中生成包含所有鍵值對的Set肯定需要開銷,所以我們在自己的實現裡面重寫了values方法,就一句話,return this.values,直接返回我們的values欄位。所以我們重寫大部分方法的目的都是讓方法的實現更快更簡潔。
大家還需要注意一下,我們在重寫entrySet方法時,需要返回一個包含當前Map所有鍵值對的Set。首先鍵值對時一種型別,所有的鍵值對類都要實現Map.Entry<K,V>這個介面。其次,由於entrySet要讓我們返回一個Set,這裡我們沒有使用Java中已有的Set型別(比如HashSet、TreeSet),有兩方面的原因:1. Java中HashSet這個類內部其實用HashMap實現的,本部落格的目的就是要研究HashMap,所以我們不用此類;2. Java中Set的實現也不是很麻煩,自己實現一下AbstractSet,加深一下對Set的理解。

以下是我們自己實現的鍵值對類SimpleEntry,實現了Map.Entry<K,V>介面,程式碼如下:


import java.util.Map;

//Map中儲存的鍵值對,鍵值對需要實現Map.Entry這個介面
public class SimpleEntry<K,V> implements Map.Entry<K, V>{
	
	private K key = null;//鍵
	
	private V value = null;//值
	
	public SimpleEntry(K k, V v){
		this.key = k;
		this.value = v;
	}

	@Override
	public K getKey() {
		return this.key;
	}

	@Override
	public V getValue() {
		return this.value;
	}

	@Override
	public V setValue(V v) {
		V oldValue = this.value;
		this.value = v;
		return oldValue;
	}
	
}

以下是我們自己實現的集合類SimpleSet,繼承自抽象類AbstractSet<K,V>,程式碼如下:
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;

public class SimpleSet<E> extends AbstractSet<E> {
	
	private ArrayList<E> list = new ArrayList<E>();

	@Override
	public Iterator<E> iterator() {
		return this.list.iterator();
	}

	@Override
	public int size() {
		return this.list.size();
	}

	@Override
	public boolean contains(Object o) {
		return this.list.contains(o);
	}

	@Override
	public boolean add(E e) {
		boolean isChanged = false;
		if(!this.list.contains(e)){
			this.list.add(e);
			isChanged = true;
		}
		return isChanged;
	}

	@Override
	public boolean remove(Object o) {
		return this.list.remove(o);
	}

	@Override
	public void clear() {
		this.list.clear();
	}

}

我們測試下我們寫的SimpleMap這個類,測試包括兩部分,一部分是測試我們寫的SimpleMap是不是正確,第二部分測試效能如何,測試程式碼如下:
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;


public class Test {

	public static void main(String[] args) {
		//測試SimpleMap的正確性
		SimpleMap<String, String> map = new SimpleMap<String, String>();
		map.put("iSpring", "27");
		System.out.println(map);
		System.out.println(map.get("iSpring"));
		System.out.println("-----------------------------");
		
		map.put("iSpring", "28");
		System.out.println(map);
		System.out.println(map.get("iSpring"));
		System.out.println("-----------------------------");
		
		map.remove("iSpring");
		System.out.println(map);
		System.out.println(map.get("iSpring"));
		System.out.println("-----------------------------");
		
		//測試效能如何
		testPerformance(map);
	}
	
	public static void testPerformance(Map<String, String> map){
		map.clear();
		
		for(int i = 0; i < 10000; i++){
			String key = "key" + i;
			String value = "value" + i;
			map.put(key, value);
		}
		
		long startTime = System.currentTimeMillis();
		
		for(int i = 0; i < 10000; i++){
			String key = "key" + i;
			map.get(key);
		}
		
		long endTime = System.currentTimeMillis();
		
		long time = endTime - startTime;
		
		System.out.println("遍歷時間:" + time + "毫秒");
	}
	
}

輸出結果如下:{iSpring=27}27-----------------------------{iSpring=28}28-----------------------------{}null-----------------------------遍歷時間:956毫秒
從結果裡面我們看到輸出結果是正確的,也就是我們寫的SimpleMap基本實現都是對的。我們往Map中插入了10000個鍵值對,我們測試的是從Map中取出這10000條鍵值對的效能開銷,也就是測試Map的遍歷的效能開銷,結果是956毫秒。沒有對比就不知效能強弱,我們測試下HashMap讀取這10000條鍵值對的時間開銷,測試方法完全一樣,只是我們傳入的是HashMap的例項,測試程式碼如下:
//建立HashMap的例項
		HashMap<String, String> map = new HashMap<String, String>();
		
		//測試效能如何
		testPerformance(map);

測試結果如下:遍歷時間:32毫秒我去,不比不知道,一比嚇一跳啊,HashMap比我們自己實現的SimpleMap快的那不是一點半點啊。為什麼我們的SimpleMap效能這麼差?而HashMap的效能如此高呢?我們分別研究。首先分析SimpleMap效能為什麼這麼差。我們的SimpleMap是用ArrayList來儲存keys和values的,ArrayList本質是用陣列實現的,我們的SimpleMap的get方法是這樣實現的:
@Override
	public V put(K key, V value) {
		V oldValue = null;
		int index = this.keys.indexOf(key);
		if(index >= 0){
			//keys中已經存在鍵key,更新key對應的value
			oldValue = this.values.get(index);
			this.values.set(index, value);
		}else{
			//keys中不存在鍵key,將key和value作為鍵值對新增進去
			this.keys.add(key);
			this.values.add(value);
		}
		return oldValue;
	}

需要效能開銷的主要是this.keys.indexOf(key)這句程式碼,這句程式碼從ArrayList中查詢指定元素的索引,本質就是從陣列開頭走,往後找,直至陣列的末尾。如下圖所示:


這樣從頭開始查詢,並且每次在遍歷元素的時候,都需要呼叫元素的equals方法,所以從頭開始查詢就會導致呼叫很多次equals方法,這就造成了SimpleMap效率低下。比如我們將全國的車輛放入到SimpleMap中時,我們是依次將車輛放到ArrayList的最後面,依次往後插入值,車牌號就相當於key,車輛就好比是value,所以SimpleMap中有兩個長度很長的ArrayList,分別儲存keys和values,如果要在該SimpleMap中查詢一輛車,車牌是"魯E.DE829",那如果用ArrayList查詢的話就要從全國的的所有車輛中去查找了,這樣太慢。那麼HashMap為何效率如此高呢?HashMap比較聰明,大家可以看看HashMash.java的原始碼,HashMap把裡面的元素分類放置了,還拿上面根據車牌號查詢車輛的例子來說,當把我們把車輛往HashMap裡面放的時候,HashMap將它們分類處理了,首先來一輛車的時候,先看其車牌號,比如車牌號是"魯E.DE829",一看是魯,就知道是山東的車輛,那麼HashMap就開闢了一塊空間,專門放山東的車,就把這輛車放到這塊山東專屬的區間了,下次又要向HashMap放入一輛車牌號為“浙A.GX588",HashMap一看是浙江的車,就將這輛車放入到浙江的專屬區間了,依次類推。說的再通俗點,假設我們有一種很大的桶,該桶就是相應的區間,可以裝下很多車,如下圖所示:
當我們從HashMap中根據車牌號查詢指定的車輛時,比如查詢車牌號為為"魯E.DE829"的車,當呼叫HashMap的get方法時,HashMap一看車牌號是魯,那麼HashMap就去標為魯的那個大桶,也就是山東區間去找這輛車了。這樣就沒有必要從全國的車輛中挨個找這輛車了,這就大大縮短了查詢空間,提高了效率。我們可以看看HashMap.java中具體的原始碼實現,HashMap中用一個名為table的欄位儲存著一個Entry陣列,table儲存著HashMap裡面的所有鍵值對,每個鍵值對都是一個Entry物件。每個Entry物件都儲存著一個key和value,除此之外每個Entry內部還存著一個next欄位,next也是Entry型別。陣列table的預設長度是DEFAULT_INITIAL_CAPACITY,即初始長度為16,當容器需要更多的空間存取Entry時,它會自動擴容。以下是HashMap的put方法的原始碼實現:
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

在put方法中,,呼叫了物件的hashCode方法,該方法返回一個int型別的值,是個初始的雜湊值,這個值就相當於車牌號,例如"魯E.DE829",HashMap中有個hash方法,該hash方法將我們得到的初始的雜湊值做進一步處理,得到最終的雜湊值,就好比我們將車牌號傳入hash方法,然後返回該存放車輛的大桶,即返回"魯",這樣HashMap就把這輛車放到標有“魯”的大桶裡面了。上面說到的hash方法叫做雜湊函式,專門負責根據傳入的值返回指定的最終雜湊值,具體實現如下:
static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

可以看出來,HashMap中主要是通過位操作符實現雜湊函式的。這裡簡單說一下雜湊函式,雜湊函式有多種實現方式,比如最簡單的就是取餘法,比如對i%10取餘,然後按照餘數建立不同的區塊或桶。比如有100個數,分別是從1到100,那麼分別對10取餘,那麼就可以把這100個數放到10個桶子裡面了,這就是所謂的雜湊函式。只不過HashMap中的hash函式看起來比較複雜,進行的是位操作,但是其作用與簡單的取餘雜湊法的作用是等價的,就是把元素分類放置。具體將鍵值對放入到HashMap中的方法是addEntry,程式碼如下:
void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

鍵值對都是Map.Entry<K,V>物件,並且Map.Entry具有next欄位,也就是桶裡面的元素都是通過單向連結串列的形式將Map.Entry串連起來的,這樣我們就可以從桶上的第一個元素通過next依次遍歷完桶裡面所有的元素。比如桶中有如下鍵值對:桶-->e1-->e2-->e3-->e4-->e5-->e6-->e7-->e8-->e9-->...addEntry程式碼首先取出桶裡面的第一個鍵值對e1,然後將新的鍵值對e置於桶中第一個元素的位置,然後將鍵值對e1放置於新鍵值對e後面,放置完之後,桶中新的鍵值對如下:桶-->e-->e1-->e2-->e3-->e4-->e5-->e6-->e7-->e8-->e9-->...
這樣就把新的鍵值對放到了桶中了,也就將鍵值對放到HashMap中了。那麼當我們從HashMap中查詢某個鍵值對時,怎麼查詢呢?原理與我們將鍵值對放入HashMap相似,以下是HashMap的get方法的原始碼實現:
public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }

在get方法中,也是先呼叫了物件的hashCode方法,就相當於車牌號,然後再將該值讓hash函式處理得到最終的雜湊值,也就是桶的索引。然後我們再去這個標有“魯”的桶裡面去找我們的鍵值對,首先先取出桶裡面第一個鍵值對,比對一下是不是我們要找的元素,如果是就直接返回了,如果不是就通過鍵值對的next順藤摸瓜通過單向連結串列繼續找下去,直至找到。  如下圖所示:

下面我們再寫一個Car類,該類有一個欄位String型別的欄位num,並且我們重寫了Car的equals方法,我們認為只要車牌號相等就認為這是同一輛車。程式碼如下所示:
import java.util.HashMap;

public class Car {
	
	private final String num;//車牌號
	
	public Car(String n){
		this.num = n;
	}
	
	public String getNum(){
		return this.num;
	}

	@Override
	public boolean equals(Object obj) {
		if(obj == null){
			return false;
		}
		if(obj instanceof Car){
			Car car = (Car)obj;
			return this.num.equals(car.num);
		}
		return false;
	}
	

	public static void main(String[] args){
		HashMap<Car, String> map = new HashMap<Car, String>();
		String num = "魯E.DE829";
		Car car1 = new Car(num);
		Car car2 = new Car(num);
		System.out.println("Car1 hash code: " + car1.hashCode());
		System.out.println("Car2 hash code: " + car2.hashCode());
		System.out.println("Car1 equals Car2: " + car1.equals(car2));
		map.put(car1, new String("Car1"));
		map.put(car2, new String("Car2"));
		System.out.println("map.size(): " + map.size());
	}

}
我們在main函式中寫了一些測試程式碼,我們建立了一個HashMap,該HashMap的用Car作為鍵,用字串作為值。我們用同一個字串例項化了兩個Car,分別為car1和car2,然後將這兩個car都放入到HashMap中,輸出結果如下:Car1 hash code: 404267176
Car2 hash code: 2027651571
Car1 equals Car2: true
map.size(): 2
從結果可以看出來,Car1和Car2是相等的,既然二者是相等的,也就是兩者作為鍵來說是相等的鍵,所以HashMap裡面只能放其中一個作為鍵,但是實際結果中map的長度卻是2個,為什麼會這樣呢?關鍵在於Car的hashCode方法,準確的說是Object的hashCode方法,Object的hashCode方法預設情況下返回的是物件記憶體地址,因為記憶體地址是唯一的。我們沒有重寫Car的hashCode方法,所以car1的hashCode返回的值和car2的hashCode返回的值肯定不同。通過我們前面研究可知,如果是兩個元素相等,那麼這兩個元素應該放到同一個HashMap的桶裡。但是由於我們的car1和car2的hashCode不同,所以HashMap將car1和car2分別放到不同的桶子裡面了,這就出問題了。相等(equals)的兩個元素(car1和car2)如果hashCode返回值不同,那麼這兩個元素就會放到HashMap不同的區間裡面。所以我們寫程式碼的時候要保證相互equals的兩個物件的雜湊值必定要相等,即必須保證hashCode的返回值相等。那如何解決這個問題?我們只需要重寫hashCode方法即可,程式碼如下:
@Override
	public int hashCode() {
		return this.num.hashCode();
	}
重新執行main中的測試程式碼,輸出結果如下:Car1 hash code: 607836628
Car2 hash code: 607836628
Car1 equals Car2: true
map.size(): 1
之前我們說了,相互equals的物件必須返回相同的雜湊值,相同雜湊值的物件都在一個桶裡面,但是反過來,具有相同雜湊值的物件(也就是在同一個桶裡面的物件)不必相互equals。總結:1. HashMap為了提高查詢的效率使用了分塊查詢的原理,物件的hashCode返回的雜湊值進行進一步處理,這樣就有規律的把不同的元素放到了不同的區塊或桶中。下次查詢該物件的時候,還是計算其雜湊值,根據雜湊值確定區塊或桶,然後在這個小範圍內查詢元素,這樣就快多了。2. 如果重寫了equals方法,那麼必須重寫hashCode方法,保證如果兩個物件相互equals,那麼二者的hashCode的返回值必定相等。3. 如果兩個物件的hashCode返回值相等,這兩個物件不必是equals的。


相關推薦

深入理解java註解的實現原理(轉載)

轉自:深入理解java註解的實現原理 今天將從以下4個方面來系統的學習一下java註解 什麼是註解 註解的用途 註解使用演示 註解的實現原理 1,什麼是註解 註解也叫元資料,例如我們常見的@Override和@Deprecated,註解是J

深入理解javaHashMap的使用

當你自己建立用作hashMap的鍵的類,用可能會忘記其中需要覆蓋的一些必須方法,而這種會是一個致命的錯誤 例如,有兩個物件,將Person物件與Dog物件聯絡起來,這看起來很簡單,使用Person作為鍵,Dog作為值: public class Pers

深入理解JavaHashMap實現原理

HashMap繼承自抽象類AbstractMap,抽象類AbstractMap實現了Map介面。關係圖如下所示:Java中的Map<key, value>介面允許我們將一個物件作為key,也就是可以用一個物件作為key去查詢另一個物件。在我們探討HashMap的實

深入理解Java的底層阻塞原理實現

更多 安全 posix pla static events time() 方便 原理 談到阻塞,相信大家都不會陌生了。阻塞的應用場景真的多得不要不要的,比如 生產-消費模式,限流統計等等。什麽 ArrayBlockingQueue、 LinkedBlockingQueue、

通過ArrayList原始碼深入理解javaIterator迭代器的實現原理

注意:本文將著重從原始碼的角度對Iterator的實現進行講解,不討論List與Iterator介面的具體使用方法。不過看懂原始碼後,使用也就不是什麼問題了。 java中各種實現Iterator的類所具體使用的實現方法各不相同,但是都大同小異。因此本文將只通過

理解JavaHashMap的工作原理

Java中的HashMap使用雜湊來高效的查詢和儲存值。HashMap內部使用Map.Entry的形式來儲存key和value, 使用put(key,value)方法儲存值,使用get(key)方法查詢值。 理解hashCode() Java中的hashCode()

深入理解JDK的Reference原理和原始碼實現

前提 這篇文章主要基於JDK11的原始碼和最近翻看的《深入理解Java虛擬機器-2nd》一書的部分內容,對JDK11中的Reference(引用)做一些總結。值得注意的是,通過筆者對比一下JDK11和JDK8對於java.lang.ref包的相關實現,發現程式碼變化比較大,因此本文的原始碼分析可能並不適合於J

1.Java集合-HashMap實現原理及源碼分析

int -1 詳細 鏈接 理解 dac hash函數 順序存儲結構 對象儲存   哈希表(Hash Table)也叫散列表,是一種非常重要的數據結構,應用場景及其豐富,許多緩存技術(比如memcached)的核心其實就是在內存中維護一張大的哈希表,而HashMap的實

JDK學習---深入理解java的String

test bound test6 -h 很多 lai 靈活性 圖形 會有 本文參考資料: 1、《深入理解jvm虛擬機》 2、《大話數據結構》、《大化設計模式》 3、http://www.cnblogs.com/ITtangtang/p/3976820.html#344102

深入理解 Java的 流 (Stream)

重要 抽象 bool sta 也會 簡單 throws image true 首先,流是什麽? 流是個抽象的概念,是對輸入輸出設備的抽象,Java程序中,對於數據的輸入/輸出操作都是以“流”的方式進行。設備可以是文件,網絡,內存等。 流具有方向性,至於是輸入流還是輸出流則

深入理解Java的逃逸分析

end 代碼 堆內存 解決 永遠 例子 append 解釋器 return 在Java的編譯體系中,一個Java的源代碼文件變成計算機可執行的機器指令的過程中,需要經過兩段編譯,第一段是把.java文件轉換成.class文件。第二段編譯是把.class轉換成機器指令的過程。

深入理解Java的字段與屬性的區別

ring rgs name 私有變量 pub tail 博文 們的 方式 轉載出處 http://blog.csdn.net/chenchunlin526/article/details/69939337 1、Java中的屬性和字段有什麽區別? 答:Java中的屬性(p

深入理解JAVA的NIO

類文件 邏輯 不同的 字節轉換 實現類 讀取 組件 進行 大量 前言: 傳統的 IO 流還是有很多缺陷的,尤其它的阻塞性加上磁盤讀寫本來就慢,會導致 CPU 使用效率大大降低。 所以,jdk 1.4 發布了 NIO 包,NIO 的文件讀寫設計顛覆了傳統 IO 的設計,采用通

深入理解Java的volatile關鍵字

語言 重新 為什麽 設置 模型 可見性 會有 普通 enter 在再有人問你Java內存模型是什麽,就把這篇文章發給他中我們曾經介紹過,Java語言為了解決並發編程中存在的原子性、可見性和有序性問題,提供了一系列和並發處理相關的關鍵字,比如synchronized、vola

深入理解Volatile關鍵字及其實現原理

volatile的用法 volatile通常被比喻成"輕量級的synchronized",也是Java併發程式設計中比較重要的一個關鍵字。和synchronized不同,volatile是一個變數修飾符,只能用來修飾變數。無法修飾方法及程式碼塊等。 volatile的用法比較簡單,只需要在

深入理解Java的同步靜態方法和synchronized(class)程式碼塊的類鎖 深入理解Java併發synchronized同步化的程式碼塊不是this物件時的操作

一.回顧學習內容  在前面幾篇部落格中我我們已經理解了synchronized物件鎖、物件鎖的重入、synchronized方法塊、synchronized非本物件的程式碼塊,  連結:https://www.cnblogs.com/SAM-CJM/category/1314992.h

深入理解Java的synchronized鎖重入

問題匯入:如果一個執行緒呼叫了一個物件的同步方法,那麼他還能不能在呼叫這個物件的另外一個同步方法呢? 這裡就是synchronized鎖重入問題。 一.synchronized鎖重入  來看下面的程式碼: .這個是三個同步方法的類 public class Syn

深入理解Java停止執行緒

一.停止執行緒會帶來什麼? 對於單執行緒中,停止單執行緒就是直接使用關鍵字return或者break,但是在停止多執行緒時是讓執行緒在完成任務前去開啟另外一條執行緒,必須放棄當前任務,而這個過程是不可預測,所以必須去做好防備。 二.認識停止執行緒的幾個方法  2.1三個被棄用的方法 &n

深入理解Java的fail-fast和fail-safe

什麼是快速失敗(fail-fast)和安全失敗(fail-safe)?它們又和什麼內容有關係。以上兩點就是這篇文章的內容,廢話不多話,正文請慢用。 我們都接觸 HashMap、ArrayList 這些集合類,這些在 java.util 包的集合類就都是快速失敗的;而  java.ut

深入理解 Java 的 try-with-resource

背景 眾所周知,所有被開啟的系統資源,比如流、檔案或者Socket連線等,都需要被開發者手動關閉,否則隨著程式的不斷執行,資源洩露將會累積成重大的生產事故。 在Java的江湖中,存在著一種名為finally的功夫,它可以保證當你習武走火入魔之時,還可以