单例模式

定义

保证一个类只有一个实例,并且提供一个访问该全局访问点

优缺点

优点:

  • 一个类只有一个实例:防止其他对象对自己的实例化,确保所有的对象都访问一个实例。
  • 有一定的伸缩性:类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩。
  • 提供了对唯一实例的受控访问。
  • 因为在系统内存种只存在一个对象,因此可以节约资源。
  • 允许可变数目的实例。
  • 避免对共享资源的多重占用。

缺点

  • 不适用于变化的对象
  • 单例模式没有抽象层,所以单例类的扩展有很大的困难。
  • 单例类职责过重,在一定程度上违背了单一职责原则。
  • 滥用单例会造成一些负面影响:为了节省资源将数据库连接池对象设计为的单例类,可能会导致,共享连接池对象的程序过多而出现连接池溢出。
  • 如果实例化的对象长时间不被利用,系统会认为是 垃圾而被回收,这将导致对象状态的丢失。

应用

windows的任务管理器、回收站

任务管理器它不能打开两个

线程池:需要方便对池种的线程进行控制。

对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。

网站计数器:如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的

设计思想:

  1. 不允许其他程序用new对象(new就是开辟新的空间,每一次new都产生一个对象)
  2. 在该类中创建对象(这里的对象需要在本类中new出来)
  3. 对外提供一个可以让其他程序获取该对象的方法(因为对象是在本类中创建的,所以需要提供一个方法让其它的类获取这个对象)

思想实现

(1)私有化该类的构造函数
(2)通过new在本类中创建一个本类对象
(3)定义一个公有的方法,将在该类中所创建的对象返回

饿汉式:类初始化,就会加载对象

public class Singleton {
    //通过new在本类中创建一个本类对象
    private static Singleton instance=new Singleton();
    //构造方法私有
    private Singleton(){

    };
    //定义一个公有的方法,将在该类中所创建的对象返回
    public static Singleton getInstance(){
        return instance;
    }
    
     public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

  • 在类加载的时候就完成了实例化,避免了线程的同步问题

  • 由于在类加载的时候就实例化了,所以没有达到Lazy Loading(懒加载)的效果,也就是说可能我没有用到这个实例,但是它也会加载,会造成内存的浪费(但是这个浪费可以忽略,所以这种方式也是推荐使用的)

懒汉式:调用getInstance方法的时候才会创建对象

这种方式是在调用getInstance方法的时候才创建对象的,所以它比较懒因此被称为懒汉式

public class Singleton {
	private static Singleton instance;
	private Singleton() {
        
    };
	public synchronized static Singleton getInstance(){
		if(instance==null){
			instance=new Singleton();
		}
		return instance;
	}
    	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1 == s2);
	}
}

如果不加synchronized锁:

此方式存在线程安全问题:

多个线程执行这个方法的时候会实例化多个对象

当第一个线程在执行if(instance==null)这个语句时,此时instance是为null的进入语句。

在还没有执行instance=new Singleton()时(此时instance是为null的)

第二个线程也进入if(instance==null)这个语句,

因为之前进入这个语句的线程中还没有执行instance=new Singleton(),

所以它会执行instance=new Singleton()来实例化Singleton对象,

因为第二个线程也进入了if语句所以它也会实例化Singleton对象。

这样就导致了实例化了两个Singleton对象。

所以单例模式的懒汉式是存在线程安全问题的

静态内部类[推荐用]

public class Singleton{
	private Singleton() {
        
    };
	private static class SingletonHolder{
		private static Singleton instance=new Singleton();
	}
	public static Singleton getInstance(){
		return SingletonHolder.instance;
	}
}
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		System.out.println(s1 == s2);
	}

​ 这里结合了懒汉式和饿汉式的优点,真正需要对象的时候才会加载,并且加载类线程是安全的。

⑤枚举[极推荐使用]

public class Singleton {
    
    public static Singleton getInstance(){
        return SingletonEnum.instance.getInstance();
    }
    
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }

    private static enum SingletonEnum {
        instance;
        private Singleton singleton;
        private SingletonEnum(){
            singleton = new Singleton();
        }
        public Singleton getInstance(){
            return singleton;
        }
    }
}

实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞,

缺点没有延迟加载。

posted @   大菠萝zZ  阅读(19)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示