设计模式:是一套被反复使用,多数人知晓,经过分目编类的,代码设计经验的总结。
目的:使用设计模式是为了可重用代码,让代码更容易被他人所理解,保证代码的可靠性
单例模式:一个类只有一个实例,并提供全局访问点
单例模式两大类基础实现:
(1)饿汉模式:实例在类加载的时候就被创建,线程安全,性能比较低,创建慢但是加载快,加载过早的话可能会浪费资源
private static Singleton uniqueInstance = new Singleton();
private Singleton(){}
public static Singleton getUniqueInstance(){
return uniqueInstance;
}
(2)懒汉模式:实例在第一次被使用时创建,非线程安全
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
单例模式当中的线程安全:
上文中的懒汉式实现方法,在单线程的时候不会有任何问题,但是在多线程的情况下,当两个线程同时执行到if语句的时候,如果此时都判断uniqueInstance==null(第一个线程还未创建完实例,第二个线程就进入判断语句),那么两个线程会各自创建一个实例,就破坏了单例模式的实例唯一性。
如何解决线程安全问题:
首先我们想到的会是加Synchronized关键字,加上同步锁实现线程安全,这样如果存在两个线程同时进入方法,那么只有一个线程可能获得同步锁,而另一个线程则需要等待,直到第一个线程释放锁(即完成现成的创建或者返回),第二个线程才有机会获得锁,这样就避免了因为第一个线程未执行完毕而第二个线程判断uniqueInstance==null,从而创建两个实例的情况,但是这种方法很显然不够高效,因为只有在创建线程时才会出现线程安全性问题,如果实例已经创建只是单纯的返回,则不需要加锁。具体实现如下:
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
synchronized(Singleton.class){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
为了更加高效的实现线程安全,可以使用双重检查(Double-Check),即上一种方法的一种优化
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
为什么要用两重检查而不是一重检查
第一个if是为了提高效率,只有当uniqueInstance==null时才会加锁,大大减少了进入锁的概率
第二个if则是为了防止重复创建实例,
如果只有第一个锁的话,还是没有办法防止重复创建实例
上面实现双重检查方法所隐藏的问题:
因为计算机为了提高效率,会进行一些优化,比如java内存模型会进行指令重排序
在上面的代码中 uniqueInstance = new Singleton();并非原子操作,JVM会进行三步操作:
1)给Singleton分配内存
2)调用Singleton的构造函数来初始化成员变量,形成实例
3)将Singleton对象指向分配的内存空间
但是由于存在指令重排序问题,2,3步有可能颠倒执行,如果这样的话,当第一个线程执行到3但是未执行2时,第二个线程进来,判断uniqueInstance!=null,直接返回Instance,但是这是线程1对Instance的操作还未完成,所以会产生错误
为了解决这样的错误,给UniqueInstance添加一个Volatile关键字,volatile具有禁止指令重排序问题
private volatile static Singleton uniqueInstance;
其他方法实现单例模式:
1)静态内部类:利用类加载机制保证创建一个实例,线程安全,并且没有性能损耗
public Singleton() {
// TODO Auto-generated constructor stub
}
private static class sigletonL{
private static final Singleton2 uniqueInstance = new Singleton2();
}
public static Singleton2 getUniqueInstance(){
return sigletonL.uniqueInstance;
}
2)枚举 简单,高效,安全
public enum Singleton{
UNIQUEINSTANCE;
public void func(){
}
}
单例模式的优点:
1)在内存中只有一个对象,节约资源
2) 避免繁琐的创建销毁对象,可以提高性能
3)避免对共享资源的多重占用
4)可以全局访问
单例模式的缺点:
1)扩展困难
2)导致程序内存泄漏的问题