什么是单例模式,为什么需要单例模式?
单例模式,就是在运行时域,一个类只有一个实例对象。
有时候一个类的创建和销毁对资源的消耗特别大,并且如果这些类是完全可以复用的话,那么将会造成不必要的性能浪费
比如数据库连接的操作,创建连接对象是一个消耗资源的操作,并且这个操作完全是可以复用的,那么就可以将这个对象设计成一个单例的,这样我就可以创建一次,并且重复使用这个对象就可以了
java中如何实现单例模式
写单例模式主要考虑三点
1、是不是懒加载
2、线程是不是安全
3、能不能通过反射破坏
双检锁写法
public class Singletone{
private Singleton(){} //构造器私有,这样其它类就不能直接new了
private static Singleton insetance = nell; //初始化对象为null
public static Single getInstance(){
if(instance == null){
instance =new Singleton();
}
return instance;
}
}
这种实例对象实在第一次被调用的时候下真正构建的,而不是程序启动的时候就构建好了等着你调用的,这种之后构建的方式就叫做懒加载
有的对象的构建开销是比较大的,加入这个对象在启动的时候就构建万一没被调用就浪费了,这就能体现出懒加载的优势
但是此时的线程是不安全的,可以在第一次创建对象的时候加锁,以后会跳过这一步
public class Singletone{
private Singleton(){} //构造器私有,这样其它类就不能直接new了
private static Singleton singleton = nell; //初始化对象为null
public static Single getInstance(){ // 1
if(singleton == null){ // 2
synchronized(Singletone.class){ //3
singleton =new Singleton(); //4
}
}
return instance;
}
}
如果有多个线程同是进入 if 语句中,那么就会创建多个线程,这也是不好的,所以我们在加入一个判空,构成**双检锁**就可以解决
public class Singletone{
private Singleton(){} //构造器私有,这样其它类就不能直接new了
private static Singleton singleton = nell; //初始化对象为null
public static Single getInstance(){
if(singleton == null){
synchronized(Singletone.class){
if(singleton == null){
singleton =new Singleton();
}
}
}
return instance;
}
}
此时不满足java多线程的happens-before 原则(定义了多线程可见性问题)
因为在singleton =new Singleton(); 在指令层面不是一个原子的操作,她分为了三步1、分配内存 2、初始化对象 3、对象指向内存地址,在真正执行是,jvm虚拟机微课效率可能会对指令进行重排,同样会产生线程不安全的问题,此时加上一个volatile 关键字就能阻止作用在singleton 对象上的指令重排问题下。面写法满足了懒加载,阻止了指令重排,线程安全,叫双检锁写法 缺点是写起来比较复杂
public class Singletone{
private Singleton(){} //构造器私有,这样其它类就不能直接new了
private volatile static Singleton singleton = nell; //初始化对象为null
public static Single getInstance(){
if(singleton == null){
synchronized(Singletone.class){
if(singleton == null){
singleton =new Singleton();
}
}
}
return instance;
}
}
静态内部类写法
public class Singleton{
private static class SingletonHolder{
private static final Single INSTANCE = new Singleton();
}
private Single(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE ;
}
}
静态内部类在程序启动时不会加载,只有在第一次被调用时候才会加载(利用jdk加载机制的特性来实现懒加载)
以上的两种方式会被反射破坏(人为破坏)可以考虑枚举类,同是枚举类是线程安全的,但是不能满足懒加载,所以现在没有方法满足三种要求