WeakhashMap源码1

弱引用(WeakReference)的特性是:当gc线程发现某个对象只有弱引用指向它,那么就会将其销毁并回收内存WeakReference也会被加入到引用队列queue中。 

它的特殊之处在于 WeakHashMap 里的entry可能会被GC自动删除,即使程序员没有调用remove()或者clear()方法。

可能发生如下情况:

  • 调用两次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>());

如果存放在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();    //return null 那么new EntrySet()就没有元素了。
        }
        @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接口提供的方法

 

posted @ 2019-06-13 11:22  无天666  阅读(327)  评论(0编辑  收藏  举报