GOF23
单例模式
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点(获得该类实例的方法)
常见引用场景:
- windows Task Manager(任务管理器)
- 项目中,读取配置文件的类 >> 没必要每次使用配置文件数据都 new 一个对象去读取
- 网站计数器 >> 一般采用单例模式,否则难以同步
- 应用程序的日志应用 >> 一般采用单例模式,因为共享的日志文件一直处于打开的状态,只能有一个实例去操作,否则内容不好追加
- 数据库连接池 >> 因为数据库连接是一种数据库资源
- 操作系统的文件系统 >> 也是最大的单例模式的具体例子,一个操作系统只能有一个文件系统
- Application(SerlvetContext) 和 Servlet 也是单例
- Spring 每个默认的 Bean 就是单例,这样做的有点是 方便 ioc 容器管理
- Spring MVC/Struts1 框架中,控制对象也是单例
优点
- 由于单例模式只生成一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,如读取配置文件,产生其他的依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
- 单例模式可以在系统设置全局的访问点,优化环境共享资源访问
常见的五种单例模式实现方式
- 主要:
饿汉式(线程安全, 调用效率高. 但是,不能延时加载)
懒汉式(线程安全, 调用效率不高, 但是 可以延时加载) - 其他
双重检测锁式(由于jvm 底层内部模型原因,偶尔会出问题,不建议使用)
静态内部类式(现成安全, 调用效率高, 可以延时加载)
枚举单例(线程安全, 调用效率高, 不能延时加载)
exmaple: 饿汉式
public class Damo {
public static void main(String[] args) {
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
}
}
class SingletonDamo {
//类初始化时,立即加载这个对象,加载类时,天然的现成安全!
private static final SingletonDamo s = new SingletonDamo();
// 构造方法私有化
private SingletonDamo() {
}
// 对外开放获得实例的静态方法
public static SingletonDamo getSingleton(){
return s;
}
}
exmaple : 懒汉式 (lazy load)
public class Damo {
public static void main(String[] args) {
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
}
}
class SingletonDamo {
//类初始化时,不初始化这个对象(延时加载,真正用到时在加载)
private static SingletonDamo s;
// 构造方法私有化
private SingletonDamo() {
}
//加上 synchronized 避免多线程时创建多个对象
public static synchronized SingletonDamo getSingleton(){
if (s == null)
s = new SingletonDamo();
return s;
}
}
exmaple : 双重检测锁式 (不推荐使用)
public class Damo {
public static void main(String[] args) {
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
System.out.println("SingletonDamo.getSingleton() = " + SingletonDamo.getSingleton());
}
}
class SingletonDamo {
private static SingletonDamo s;
// 构造方法私有化
private SingletonDamo() {
}
//加上 synchronized 避免多线程时创建多个对象
public static SingletonDamo getSingleton(){
if (s == null){
SingletonDamo temp;
synchronized (SingletonDamo.class){
temp = s;
if (temp == null){
synchronized (SingletonDamo.class){
if (temp == null){
temp = new SingletonDamo();
}
}
s = temp;
}
}
}
return s;
}
}
exmaple : 静态内部类 (推荐使用)
class SingletonDamo {
public static SingletonDamo getInstance(){
return SingletonInstance.
getSingletonDamo();
}
private static class SingletonInstance{
private static final SingletonDamo instance = new SingletonDamo();
private SingletonInstance() {
}
public static SingletonDamo getSingletonDamo(){
return SingletonInstance.instance;
}
}
}
exmaple : 枚举式
public enum SingletonEnum {
INSTANCE;
public void show(){
System.out.println("this = " + this);
}
}
反射 和 反序列化漏洞
- 反射
在构造方法中 手动抛出异常
if(instance != null){
throw new RuntimeException("不能通过反射实例化");
}
- 反序列化
private Object readResolve() thirows ObjectStreamException(){
return instance;
}