为什么说单例模式性价比高?
在面试过程中经常让手撸的一种设计模式,并且被面的频率非常高。重点是这种设计模式很简单!
我的个人博客(后台建站,Tomcat集群,Redis分布式,nginx,适合初学者)
为什么要用单例模式?
在开发过程中,根据需求的不同,有时可能要求对象实例只能有一个,这时就要使用到了单例模式。
如:Spring中的bean默认使用的都是单例模式。
什么是单例模式?
确保对象只有一个,并提供一个全局访问点。
一、单例模式初探
经典的实现方式:
饿汉式
public class Singleton1{
private static Singleton1 uniqueInstance = new Singleton1();
private Singleton1() {
super();
}
public static Singleton1 getInstance(){
return uniqueInstance;
}
}
懒汉式
public class Singleton2{
private static Singleton2 uniqueInstance = null;
private Singleton2(){
super();
}
public static Singleton2 getSinleton(){
if (uniqueInstance == null) {
uniqueInstance = new Singleton2();
}
return uniqueInstance;
}
}
进阶实现方式
双重检查加锁
public class Singleton3{
private static volatile Singleton3 uniqueInstance = null;
private Singleton3 (){
super();
}
public static Singleton3 getInstance() {
if (uniqueInstance == null){
synchronized (Singleton3.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton3();
}
}
}
return uniqueInstance;
}
}
静态内部类
public class Singleton4{
private Singleton4() {
super();
}
private static class Holder{
private static final Singleton4 uniqueInstance = new Singleton4();
}
public static final Singleton4 getInstance(){
return Holder.uniqueInstance;
}
}
二、分析与比较
饿汉式: 在类加载时就创建对象,无论是否使用都会创建,天生线程安全。
懒汉式:延迟化实例,在使用时才会创建,线程不安全,相比饿汉式来说对于资源敏感的对象很重要,能节省系统资源。
双重校验:延迟化实例,线程安全,但是牺牲性能来提升安全。
静态内部类:只有在,内部类在第一次调用Holder.uniqueInstance时才会创建实例,所以也是一种延迟化实例的机制,通过静态内部类只有第一次引用才会被加载,所以是线程安全的;并且通过反射,也无法从外部类获取内部类的属性。所以这种形式,很好的避免了反射入侵。
三、总结
为什么进行双重校验?
由于同步范围比较精确,也可能出现安全问题,即在判断为null和加锁之间另一个线程已经创建了对象,因为使用了volitile关键字保证了对象的可见性,所以必须在此进行二次检查,否则式去了同步的意义。
如何选择适当的方案?
①系统是否是资源敏感型?
如果对系统资源不敏感,懒汉式式一种不错的选择!
②是否有性能上考虑?
如果没有性能上考虑,可以使用懒汉式!
如果有性能考虑,应使用双重检查加锁或静态内部类!
注:静态内部类也有一些缺点,需要两个类才能实现,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久代的对象。