单例模式核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
- 常见场景:
(1)Windows的任务管理器和回收站,无论点击多少次都只有一个窗口
(2)项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
(3)网站的计数器为了保证同步一般也会采用单例模式
(4)数据库的连接池设计
(5)Servlet编程中,每个Servlet也是单例的
(6)在Spring中,每个Bean的默认就是单例的
优点
- 由于只生成一个实例,减少了系统系能的开销
- 单例模式可以在系统设置全局的访问点,优化共享资源访问
- 单例模式可以说只要是一个合格的开发者都会写,但是如果要深究,小小的单例模式会牵扯到很多东西
常见的物种单例模式实现方式
- 饿汉式(线程安全,调用效率高,不能延时加载)
- 懒汉式(线程安全,调用效率不高,可以延时加载)
- DCL懒汉式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用
- 饿汉式改进:静态内部类式(线程安全,调用效率高,能延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载)
代码实现
- 饿汉式,即无论有没有用都先初始化,而这也会导致空间的浪费,那么我们就希望我们在调用到里面的
getInstance()
获取实例的时候,再去加载,即延时加载public class SingletonDemo01{ //首先私有化构造器,让对象私有,保证只有一个对象,不可new private SingletonDemo01(){ } //类在初始化的时候就加载对象 private static SingletonDemo01 instance = new SingletonDemo01(); //提供一个全局访问点,没有synchronized,效率更高 public static SingletonDemo01 getInstance(){ return instance; } }
- 懒汉式,调用的时候才会去实例化
public class SingletonDemo02{ //首先私有化构造器,让对象私有,保证只有一个对象,不可new private SingletonDemo02(){ } //类初始化的时候,不立即加载 private static SingletonDemo02 instance; //提供一个全局访问点,synchronized使其成为同步方法,效率较低 public static synchronized SingletonDemo02 getInstance(){ if (instance == null){ instance = new SingletonDemo02(); } return instance; } }
- DCL懒汉式,改进了
synchronized
,DCL即双重检测锁。如果线程刚进来的时候发现没有被创建,为空,就会去和其他线程竞争锁,当竞争得到锁之后,再去检查是否还为空,如果还为空就再去new一个,如果不为空就直接返回。public class SingletonDemo03{ //首先私有化构造器,让对象私有,保证只有一个对象,不可new private SingletonDemo03(){ } //类初始化的时候,不立即加载 //volatile 保证有线程在对这个变量进行修改的时候,另一个线程中该变量的缓存就会失效 //volatile 增加了指令的重排,避免第一个线程还没创建完成但第二个进程已经返回的问题 //保证了原子性 private volatile static SingletonDemo03 instance; //提供一个全局访问点,synchronized使其成为同步方法,效率较低 public static SingletonDemo03 getInstance(){ if (instance == null){ synchronized(SingletonDemo03.class){ if(instance == null){ instance = new SingletonDemo03(); } } } return instance; } }
- 饿汉式改进,使用静态内部类,外部类没有属性,只有当调用
getInstance()
的时候才加载静态内部类,加载的时候线程是安全的,final关键字保证了内存中只有一个实例存在,保证了并发情况下的高效率调用还可以延时加载
但是通过Java的反射机制可以破坏掉public class SingletonDemo04{ private SingletonDemo04(){ } private static class InnerClass{ private static final SingletonDemo04 instance = new SingletonDemo04(); } public static SingletonDemo04 getInstance(){ return InnerClass.instance; } }
private
,可以通过反射机制忽视掉权限检查,这样就能破坏掉private,就可以在外部new一个不同的
- 枚举式
可知,利用枚举,能很好的保证线程安全public enum SingletonDemo05 { INSTANCE; public SingletonDemo05 getInstance(){ return INSTANCE; } }