一、什么是单例设计模式
单例设计模式的定义是只有一个类,并且提供一个全局访问点。
二、适用于那些场景
一个对象即可完成所有工作,无需大量创建对象消耗资源。比如一个长连接,建立起来就不断的发送数据,如果每一个请求都创建一个链接,资源很快就被消耗殆尽。
三、有什么特点
只有一个实例
自我实例化
提供一个全局访问点
四、单例模式的优缺点
优点:由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。
缺点:也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难
五、如何创建
方法一 饿汉模式
使用静态常量,在单例类加载的时候就创建了,常见代码如下:
classSingleton1 {//类内部创建实例
private static Singleton1 instance=newSingleton1();//构造方法私有化,防止通过new方式 创建私立
privateSingleton1(){}//对外提供全局同用的唯一调用方法
public staticSingleton1 getInstance(){returninstance;
}
}
这种方式优点缺点都很明显,
优点:类加载的时候实例化,防止对线程同时访问问题。
缺点:类加载时候就实例化,如果没有使用就会造成内存空间的浪费。
方法二懒汉模式
为了解决饿汉模式对于不使用造成内存空间浪费的缺点,又有人提出了懒汉模式,懒汉模式就是在使用时才创建实例,如果不使用就不会创建。
public classSingleton2 {//先声明句柄,但不立即创建实例
private staticSingleton2 instance;//构造方法私有化,防止外部通过使用new方式创建实例
privateSingleton2(){}//想外部提供全局共享的唯一实例化接口方法
public staticSingleton2 getInstance(){if(instance==null){
instance=newSingleton2();
}returninstance;
}
}
但这种懒汉模式也存在一个很大的问题,它有多线程问题,在多个线程同时访问的时候并不能保证单例,我们使用多线程去获取单例,发现获取的实例并不唯一。
public classmytest {public static voidmain(String[] args){long l1=System.currentTimeMillis();
ExecutorService es=Executors.newFixedThreadPool(10);for(int i=0;i<10;i++){
es.execute(newRunnable(){
@Overridepublic voidrun(){
Singleton2 s2=Singleton2.getInstance();
System.out.println(s2);
}
});
}
es.shutdown();while(!es.isTerminated()){}long l2=System.currentTimeMillis();
System.out.println(l2-l1);
}
}
zc.com.examp.test1.core.Singleton2@1ffffbce
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@1ffffbce
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
5
这是因为在执行instance==null语句时,此时还没有创建实例,此时满足条件的线程都进入,执行instance=new Singleton2()语句。对于此问题,我们可以使用synchronized关键字,让getInstance()方法只能同时被一个线程访问。
public classSingleton2 {//先声明句柄,但不立即创建实例
private staticSingleton2 instance;//构造方法私有化,防止外部通过使用new方式创建实例
privateSingleton2(){}//想外部提供全局共享的唯一实例化接口方法
public static synchronizedSingleton2 getInstance(){if(instance==null){
instance=newSingleton2();
}returninstance;
}
}
测试结果如下:
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
zc.com.examp.test1.core.Singleton2@49ce13ed
9
可以看到通过使用Synchronized关键字确实可以解决多线程同时访问的问题,但同时也降低了执行效率,每次调用getInstance()方法都要涉及锁的操作。其实我们可以对上面的代码进一步优化。
public classSingleton2 {//先声明句柄,但不立即创建实例
private staticSingleton2 instance;//构造方法私有化,防止外部通过使用new方式创建实例
privateSingleton2(){}//想外部提供全局共享的唯一实例化接口方法
public staticSingleton2 getInstance(){if(instance==null){//只有在第一次使用的时候构造实例对象,使用synchronized代码块和双重判断避免多线程问题,并且提供效率
synchronized(Singleton2.class){if(instance==null){
instance=newSingleton2();
}
}
}returninstance;
}
}
测试结果:
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
zc.com.examp.test1.core.Singleton2@4c75281a
5
这种方式不仅解决了线程不安全问题,又提升了执行效率,只有前面的少数线程可能会获取锁,只要实例被创建,后面的线程一般只需第一个if判断就返回了对象,所以这种方式推荐使用。
方法三内部静态类。
我们知道饿汉模式缺点就是类在加载时候就创建了实例,容易造成内存资源的浪费,如果在单例类中再创建一个静态内部类,外部类装载的时候静态内部类不会装载,只有使用的时候才会装载,因此达成了懒汉式的效果,实现代码如下:
classSingleton3 {//构造方法私有化,防止通过new方式 创建私立
privateSingleton3(){}//静态内部类,在外部类加载的时候不会加载静态内部类
private static classSingletonInstance{static Singleton3 instance=newSingleton3();
}//对外提供全局同用的唯一调用方法
public staticSingleton3 getInstance(){//只有在使用到静态内部类的时候才会加载,并且通过类加载机制保证在初始化的时候只有一个实例产生
returnSingletonInstance.instance;
}
}
-----------------------------------------------------------------------------------------------------------------