WeakhashMap源碼1

弱引用(WeakReference)的特性是:當gc線程發現某個對象只有弱引用指向它,那麼就會將其銷燬並回收內存WeakReference也會被加入到引用隊列queue中。 java

它的特殊之處在於 WeakHashMap 裏的entry可能會被GC自動刪除,即便程序員沒有調用remove()或者clear()方法。程序員

可能發生以下狀況:api

  • 調用兩次size()方法返回不一樣的值;
  • 兩次調用isEmpty()方法,第一次返回false,第二次返回true
  • 兩次調用containsKey()方法,第一次返回true,第二次返回false,儘管兩次使用的是同一個key
  • 兩次調用get()方法,第一次返回一個value,第二次返回null,儘管兩次使用的是同一個對象。

WeekHashMap 的這個特色特別適用於須要緩存的場景。數組

GC判斷某個對象是否可被回收的依據是,是否有有效的引用指向該對象。緩存

這裏的「有效引用」並不包括弱引用。也就是說,雖然弱引用能夠用來訪問對象。安全

將一對key, value放入到 WeakHashMap 裏並不能避免該key對應的內存區域被GC回收。數據結構

既然有 WeekHashMap,是否有 WeekHashSet 呢?答案是沒有,不過Java Collections工具類給出瞭解決方案,多線程

// 將WeakHashMap包裝成一個Set框架

Set<Object> weakHashSet = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>());ide

若是存放在WeakHashMap中的key都存在強引用,那麼WeakHashMap就會退化爲HashMap

使用WeakHashMap能夠忽略容量問題,提高緩存容量。只是當容量不夠時,不會OOM,內部數據會被GC回收。命中率好像沒有辦法,容我掉一片頭髮換來深度思考後給出方案。

觀察WeakHashMap源碼能夠發現,它是線程不安全的,因此在多線程場景該怎麼辦嘞?

WeakHashMap<String, String> weakHashMapintsmaze=new WeakHashMap<String, String>();

Map<String, String> intsmaze=Collections.synchronizedMap(weakHashMapintsmaze);

public class Test11 {
    private static final int _1MB = 1024 * 1024;// 設置大小爲1MB
    public static void main(String[] args) throws InterruptedException {
        Object value = new Object();
        WeakHashMap<Object, Object> map = new WeakHashMap<Object, Object>();
        for (int i = 0; i < 100; i++) {
            byte[] bytes = new byte[_1MB];
            // bytes和value構成Entry,bytes是弱引用,沒有別人指向,就回去回收這個Entry。
            map.put(bytes, value);
        } // 其實當咱們在循環100次添加數據時,就已經開始回收弱引用了,所以咱們會看到第一次打印的size是13,而不是100,一旦GC發生,那麼弱引用就會被清除,致使WeakHashMap的大小爲0。
            //
        while (true) {
            System.gc();// 建議系統進行GC
            Thread.sleep(500);
            System.out.println(map.size());// 13 0 0 0 0
        }
    }
}
//WeakHashMap裏面EntrySet的iterator()方法返回new EntryIterator(),
//EntryIterator的next和hashNext方法是對WeakHashMap的table作的遍歷。
//因此new EntrySet(),就返回WeakHashMap的table的全部元素。

public class eee {
    @SuppressWarnings("rawtypes")
    public static void main(String[] args) {
        Collection c = new eee().entrySet();//[6, 5, 4, 3, 2, 1]
        Iterator iter = c.iterator();
        while (iter.hasNext()) {
            String entry = (String) iter.next();
            System.out.println(entry);
        }
    }
    String[] table = new String[]{"1","2","3","4","5","6"};
    String[] table1 = new String[]{"11","21","31","41","51","61"};
    private transient Set<String> entrySet;
    
    public Set<String> entrySet() { 
        Set<String> es = entrySet; 
        return es != null ? es : (entrySet = new EntrySet());//是根據hasNext()和next()方法來肯定集合元素的。
    }
    private class EntrySet extends AbstractSet<String>   { 
        public Iterator<String> iterator() {
            return new EntryIterator(); 
        }
        @Override
        public int size() {
            return 0;
        }
    }
    private class EntryIterator<String>  implements Iterator<String>   { 
        private int index; 
        EntryIterator() {
            index =  table.length; 
//            index =  table1.length; 
        }
        public boolean hasNext() {
            if (index > 0) return true;
            else return false;
        }
        public String next() {
            return (String) table[--index];
//            return (String) table1[--index];
        }
    }
}

當一個鍵對象被垃圾回收,那麼相應的值對象的引用會從Map中刪除。WeakHashMap可以節約存儲空間,可用來緩存那些非必須存在的數據。

get,put,size,isEmpty,containsKey,getEntry,resize,拷貝,putAllremovecontainsValuecontainsNullValueforEach都會清理髒數據。

keySetvaluesentrySet不會清理髒數據。

WeakHashMap是不一樣步的。可使用 Collections.synchronizedMap 方法來構造同步的 WeakHashMap。

WeakHashMap的Key是弱引用,Value不是。WeakHashMap不會自動釋放失效的弱引用tableEntry,僅當包含了expungeStaleEntries()的共有方法被調用的時候纔會釋放。

WeakHashMap沒有實現Clone和Serializable接口,因此不具備克隆和序列化的特性。

WeakHashMap由於gc的時候會把沒有強引用的key回收掉,因此註定了它裏面的元素不會太多,所以也就不須要像HashMap那樣元素多的時候轉化爲紅黑樹來處理了。

WeakHashmap將會移除一些死的(dread)的entry,避免持有過多死的弱引用。

ReferenceQuene可以輕易的追蹤這些死掉的弱引用。能夠講ReferenceQuene傳入WeakHashmap的構造方法(constructor)中,這樣,一旦這個弱引用裏面的對象成爲垃圾,這個弱引用將加入ReferenceQuene中。

    public static void main(String args[]) {
        WeakHashMap<String, String> map = new WeakHashMap<String, String>();
        map.put(new String("1"), "1");
        map.put("2", "2");
        String s = new String("3");
        map.put(s, "3");
        while (map.size() > 0) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException ignored) {
            }
            System.out.println("Map Size:" + map.size());
            System.out.println(map.get("1"));
            System.out.println(map.get("2"));
            System.out.println(map.get("3"));
            System.gc();
        }
    }
}

運行結果(一直循環當中):

Map Size:3   1 2 3

Map Size:2    null 2 3

根據String的特性,

元素「1」的key已經沒有地方引用了,因此進行了回收。

元素「2」是被放在常量池中的,因此沒有被回收。

元素「3」由於還有變量s的引用,因此也沒有進行回收。

public class ss {
    public static void main(String[] args) {
        System.out.println(test());//cde
    }
    private static String test(){
        String a = new String("a");
        System.out.println(a);//a
        WeakReference<String> b = new WeakReference<String>(a);
        System.out.println(b.get());//a
        WeakHashMap<String, Integer> weakMap = new WeakHashMap<String, Integer>();
        weakMap.put(b.get(), 1);//{a=1}
        a = null;
        System.out.println("GC前b.get():"+b.get());//a
        System.out.println("GC前weakMap:"+weakMap);//{a=1}
        System.gc();
        System.out.println("GC後"+b.get());//null,a=null; System.gc()後,b!=null,b中的a也被系統回收了,
        System.out.println("GC後"+weakMap);//{}
        String c = "";
        try{
            c = b.get().replace("a", "b");//b.get()爲null,會拋出異常。
            System.out.println("C:"+c);
            return c;
        }catch(Exception e){ 
            //finally有renturn,從finally退出。finally沒有renturn,從try退出。 //try 換成 catch 去理解就 OK 了
            c = "c";
            System.out.println("Exception");
            return c;
        }finally{
            c += "d";
            return c + "e";
        }
    }
}
public class WeakHashMapTest {  
    public static void main(String[] args) {  
        WeakHashMap w= new WeakHashMap();  
        //三個key-value中的key 都是匿名對象,沒有強引用指向該實際對象  
        w.put(new String("語文"),new String("優秀"));  
        w.put(new String("數學"), new String("及格"));  
        w.put(new String("英語"), new String("中等"));  
        //增長一個字符串的強引用  
        w.put("java", new String("特別優秀"));  
        System.out.println(w);  //{java=特別優秀, 數學=及格, 英語=中等, 語文=優秀}
        //通知垃圾回收機制來進行回收  
        System.gc();  
        System.runFinalization();  
        //再次輸出w
        System.out.println("第二次輸出:"+w);  //第二次輸出:{java=特別優秀}
    }    
}

spliteratorjava1.8引入的一種並行遍歷的機制,Iterator提供也提供了對集合數據進行遍歷的能力,但一個是順序遍歷,一個是並行遍歷。

OfInt sInt = Arrays.spliterator(arr, 2, 5);//下標,包頭不包尾。截取。

public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) { return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex, int additionalCharacteristics) { checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex); return new IntArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics); } public IntArraySpliterator(int[] array, int origin, int fence, int additionalCharacteristics) { this.array = array; this.index = origin; this.fence = fence; this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED; } public boolean tryAdvance(IntConsumer action) {//對分割後的數組依次調用函數 if (action == null) throw new NullPointerException(); if (index >= 0 && index < fence) { action.accept(array[index++]); return true; } return false; } public OfInt trySplit() {//返回分割的數組 int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : new IntArraySpliterator(array, lo, index = mid, characteristics); } public void forEachRemaining(IntConsumer action) { int[] a; int i, hi; // 每一個元素執行給定的操做 if (action == null) throw new NullPointerException(); if ((a = array).length >= (hi = fence) && (i = index) >= 0 && i < (index = hi)) { do { action.accept(a[i]); } while (++i < hi); } } public interface IntConsumer { void accept(int value); default IntConsumer andThen(IntConsumer after) { Objects.requireNonNull(after); return (int t) -> { accept(t); after.accept(t); }; } } andThen方法是由IntConsumer 對象調用的,返回值是IntConsumer 類型:一個函數,首先調用 調用andThen方法的對象的accept()方法,然後調用after的accept方法。 public boolean tryAdvance(IntConsumer action) { if (action == null) throw new NullPointerException(); if (index >= 0 && index < fence) { action.accept(array[index++]); return true; } return false; } public class ffff { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr, 2, 5);// 返回3 4 5 IntConsumer consumer = new IntConsumer() { public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer.andThen(new IntConsumer() {//先調用consumer的accept方法,在調用new IntConsumer()匿名內部類的accept方法, public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); //3 i am after,4 i am after,5 i am after } public static void main3(String[] args) { int[] arr ={ 1, 2, 3, 4, 5, 6 }; IntConsumer consumer = new IntConsumer() {// IntConsumer的accept參數只能是int @Override public void accept(int value) { System.out.println(value); } }; OfInt sInt = Arrays.spliterator(arr);// sInt = {1,2,3,4,5,6} sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 1,2,3,4,5,6 OfInt sInt1 = sInt.trySplit();// sInt1是sInt的前一半{1,2,3},sInt是後一半{4,5,6} sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 只有4 5 6 sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer);// 只有1 2 3 } public static void main2(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr, 2, 5);// 下標,包頭不包尾。截取。 IntConsumer consumer = new IntConsumer() { @Override public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); /* if (index >= 0 && index < fence) { consumer.accept(array[index++]); index和fence是sInt的屬性 */ sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 只輸出3 4 5 ,遍歷截取後的元素。 } public static void main1(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr); IntConsumer consumer = new IntConsumer() { @Override public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 輸出 1, 2, 3, 4, 5, 6, 7, 8, 9 } }

Spliterator就是爲了並行遍歷元素而設計的一個迭代器,jdk1.8中的集合框架中的數據結構都默認實現了spliterator,能夠和iterator順序遍歷迭代器一塊兒看。

Spliteratorsplitable iterator可分割迭代器)接口是Java爲了並行遍歷數據源中的元素而設計的迭代器,這個能夠類比最先Java提供的順序遍歷迭代器Iterator,但一個是順序遍歷,一個是並行遍歷

第一個方法tryAdvance就是順序處理每一個元素,相似Iterator,若是還有元素要處理,則返回true,不然返回false

第二個方法trySplit,這就是爲Spliterator專門設計的方法,區分與普通的Iterator,該方法會把當前元素劃分一部分出去建立一個新的Spliterator做爲返回,兩個Spliterator變會並行執行,若是元素個數小到沒法劃分則返回null二分法

第三個方法estimateSize,該方法用於估算還剩下多少個元素須要遍歷

第四個方法characteristics,其實就是表示該Spliterator有哪些特性,用於能夠更好控制和優化Spliterator的使用,具體屬性你能夠隨便百度到,這裏就再也不贅言

從最先Java提供順序遍歷迭代器Iterator時,那個時候仍是單核時代,但如今多核時代下,順序遍歷已經不能知足需求了...如何把多個任務分配到不一樣核上並行執行,纔是能最大發揮多核的能力,因此Spliterator應運而生啦

對於Spliterator接口的設計思想,應該要提到的是Java7的Fork/Join(分支/合併)框架,總得來講就是用遞歸的方式把並行的任務拆分紅更小的子任務,而後把每一個子任務的結果合併起來生成總體結果。帶着這個理解來看看Spliterator接口提供的方法

相關文章
相關標籤/搜索