版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18975791/article/details/83177230
问题来源
什么是单例?它的运用场景是什么?
单例模式是指保证在系统中只存在某类唯一对象。运用场景随处可见,例如工具类、Spring容器默认new对象等。
单例模式有几种实现方式?
饿汉式、懒汉式、双重检查锁式、内部类式、枚举式。
推荐使用方式?
饿汉式、内部类式。
饿汉式
饿汉式顾名思义饿,那么当应用程序一开始类加载,类的对象立马实例化加载至JVM。
public class SingletonClass {
/**
* 优点:调用效率高。
* 缺点:没有延迟加载。
*/
private static SingletonClass instance =new SingletonClass();
public static SingletonClass getInstance(){
return instance;
}
}
为什么调用效率高?没有延迟加载?
答:假设在高并发的场景下,有10W+并发调用,不需要同步处理。可以直接在堆内存直接获取对象不需要任何等待。
同样,它没有延迟加载,如果它是需要消耗很大内存的对象,最开始就加载入堆内存,而用户暂时不需要。这样就会严重占用堆内存,影响运行效率。
懒汉式
导引:脑洞大开的程序员们说:上述问题还不简单,当调用的时候在new对象不就行。于是出现了懒汉式的雏形版本。
public class SingletonClass {
private static SingletonClass instance;
public static SingletonClass getInstance(){
if(null==instance){
instance=new SingletonClass();
}
return instance;
}
}
懒汉式顾名思义懒,就是延迟加载,当被调用的时候再实例化。
问题:如果你是初出茅庐的应届生写成这样,估计面试官也不会追究什么。如果你是有一年工作年限的程序员,估计面试官就会声讨你了。假设,并发数10W+,它就将被蹂躏的不堪入目。那么我们需要怎么解决呢?加上同步操作就大功告成。
public class SingletonClass {
//调用效率低、延迟加载
private static SingletonClass instance;
public static synchronized SingletonClass getInstance(){
if(null==instance){
instance=new SingletonClass();
}
return instance;
}
}
问题:从效率维度考虑,估计这样已经完美了吧?但是,从安全纬度考虑,依然隐隐约约存在问题。如果是接触过反射、反序列化的同学,我们一起来继续探讨。
/**
* 通过反射破坏懒汉式单例
* @author aaron
*/
public class Client {
public static void main(String[] args) throws Exception {
SingletonClass clazzOne=SingletonClass.getInstance();
SingletonClass clazzTwo=SingletonClass.getInstance();
System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());
System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());
Class<SingletonClass> clazz=(Class<SingletonClass>)Class.forName("singleton.SingletonClass");
Constructor<SingletonClass> c=clazz.getConstructor(null);
c.setAccessible(true);
SingletonClass clazzThree=c.newInstance();
SingletonClass clazzFour=c.newInstance();
System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());
System.out.println("clazzFour-hasCode:"+clazzFour.hashCode());
}
}
public class SingletonClass implements Serializable{
private static SingletonClass instance;
public static synchronized SingletonClass getInstance(){
if(null==instance){
instance=new SingletonClass();
}
return instance;
}
public static void main(String[] args) throws Exception {
SingletonClass clazzOne=SingletonClass.getInstance();
SingletonClass clazzTwo=SingletonClass.getInstance();
System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());
System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());
FileOutputStream fos=new FileOutputStream(new File("f:/test.txt"));
ObjectOutputStream bos=new ObjectOutputStream(fos);
bos.writeObject(clazzOne);
bos.close();
fos.close();
FileInputStream fis=new FileInputStream(new File("f:/test.txt"));
ObjectInputStream bis=new ObjectInputStream(fis);
SingletonClass clazzThree=(SingletonClass) bis.readObject();
System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());
}
}
问题:这么轻易就被破解了?那怎么解决呢?
public class SingletonClass implements Serializable{
private static SingletonClass instance;
private SingletonClass(){
//防止被反射
if(null!=instance){
throw new RuntimeException();
}
}
public static synchronized SingletonClass getInstance(){
if(null==instance){
instance=new SingletonClass();
}
return instance;
}
//当没有定义这方法时,反序列化默认是重新new对象。
//反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
双重检查锁与内部类
双重检查锁与内部类的方式:缘由懒汉式、饿汉式要么存在调用效率低或者运行效率低问题。而这两种方式取前两者的优点为自己所用。
/**
* 单例模式-双重检查锁
* @author aaron
*/
public class SingletonClass{
private static SingletonClass instance;
public static SingletonClass getInstance(){
if(null==instance){
synchronized (SingletonClass.class) {
if(instance==null){
instance=new SingletonClass();
}
}
}
return instance;
}
}
问题:缘由JVM对于此种方式的同步控制,并不稳定,当高并发的时候,可能会出现问题,并不推荐使用这种方式。理论上来说,它是不存在问题的。
/**
* 单例模式-内部类的方式
* @author aaron
*/
public class SingletonClass{
private static class InnerClass{
public static SingletonClass instance=new SingletonClass();
}
public static SingletonClass getInstance(){
return InnerClass.instance;
}
}
/**
* 单例模式-枚举的方式
* @author aaron
*/
public enum SingletonClass{
INSTANCE
}