为啥要有单例
先说三点
- 节省资源,有些实例不需要被反复创建和初始化,特别是一些空间占用率高的。比如无状态实例,springbean一般是单例的,主要就是这个原因。
- 有些统计性的工作,比如软负载均衡器有时会设计为单例,可以统计单个jvm下负载均衡的分配情况,节点访问次数等。
- 暂时没想到
为啥单例模式里的getInstance方法要用static修饰呢
就一个原因,要保证所创建实例的唯一性,为了达到这个目的主要做了三个事:
- 构造函数用private修饰,不允许类以外的任何地方new该实例。
- 用static修饰实例对象,大家知道用static修饰的成员变量具有全局唯一性。
- 通过懒汉或者恶汉模式来保证如果存在实例就不会进行二次创建。
为啥懒汉模式需要双重检查锁定
恶汉模式咱们不说了,比较简单,主要说说懒汉的。为啥要用懒汉,首先就是恶汉这种虽然简单但是比较粗暴,需不需要都要创建,如果对象资源占用比较大就不太划算,这个时候用懒汉,可懒汉是在使用过程中创建的,就设计到一个并发问题,并发问题就又涉及到加锁后的性能问题,首先看一下关键代码。
private LazySingle(){}
private static volatile LazySingle inStance; // (1)
//双重锁方式
public static LazySingle getInstance() throws InterruptedException {
if(inStance == null){ //这一层是为了性能 (2)
synchronized (LazySingle.class){ //剩下的是为了保证唯一性
if(inStance == null){ (3)
Thread.sleep(1000);
inStance = new LazySingle();
}
}
}
return inStance;
}
在注解(1) 中我们发现用volatile 修饰了一下,目的是多线程修改后的可见性,别到时候其他线程修改了但是本线程看不到,再次创建。
为啥用双重锁就是为了提高性能,先判断下实例是否存在,如果存在那就没锁啥事了,如果没有,好我枷锁,可是在我获取锁的过程中是否会被其他线程创建呢,这个没准,所以我在注视(3)中再次判断是否为null,如果不为null说明被其他线程创建了,如果为null,你再创建实例,这个时候就完美避免了重复创建的问题,注视(3)判断在我获取锁之前是否有人创建了,那么当我获取锁之后别人也就无法创建了,所以是完整的解决掉了并发问题。
java语言的完美解答方法IoDH
通过定义静态内部类的方式完美解决了,饿汉占用资源,懒汉并发资源占用问题,实现比较简单可百度下。