private final MapsingletonObjects = new ConcurrentHashMap<>(256); private final Map > singletonFactories = new HashMap<>(16); private final Map earlySingletonObjects = new ConcurrentHashMap<>(16);
这是源码中对它的定义。
1. 是private修饰的,那么这个属性它将不能被子类访问到。
2.使用了ConcurrentHashMap。
从源码中我们可以看到对singletonObjects的修改都会有synchronized 代码块来修饰,那么为什么还需要用 ConcurrentHashMap呢?
针对这个问题进行了如下思考:
1. 虽然是private修饰的但是依然可以通过getBean等方法访问到。子类在使用的过程中使用多线程技术就可能出现并发访问。
2. 如果在多线程的情况下,其中的键值对就会出现可见性问题。如果通过HashMap来存储,A线程put了新的键值对时,B线程对这个值可能是不可见的,而ConcurrentHashMap中的K V 是用volatile修饰的,解决了可见性的问题。 这里ConcurrentHashMap作用更多的是体现在可见性上。
那么问题又来了
单例缓存池的第三层
它怎么就不用ConcurrentHashMap了呢? 我对这个问题进行了一下分析:
1. singletonFactories 字段在被访问的时候都被加上了 synchronized 同步块 所以没有原子性问题
2. singletonFactories.put() 是bean实例化后 , 执行populateBean 之前,
2.1 如果发生循环依赖则把早期对象放到二级缓存中并删除三级缓存
2.2 如果没有发生循环依赖则会在单例创建完之后被删除。
由 上述两条可见 singletonFactories 里放的东西永远也没有被其他线程访问到,那么就意味着不会有线程不安全的情况出现所以是HashMap。
既然写了这么些 顺便分析下为啥第二层是ConcurrentHashMap呢?
第二层中放的是早期对象,对早期对象的访问并不是所有地方都用同步块的,为了让别的线程也能用到早期对象而不是阻塞住,所以用ConcurrentHashMap来提供 K V可见性。
================================================================
记录一个只知道背单例创建的小白被面试官难倒的亡羊补牢



