设计模式--单例模式
简介
特点
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
实现
单例模式有懒汉式、饿汉式、双层校验锁模式,额外的:ENUM 枚举也是一种单例
饿汉式(线程安全)
/**
* @className Singleton1
* @author:yadmire
* @create: 2022-05-15 16:17
* @Description: 饿汉式单例 Demo
* 优点:没有加锁,执行效率会提高。
* 缺点:类加载时就初始化,浪费内存
*/
public class Singleton1 {
//私有化构造,不允许新建对象
private Singleton1(){
}
private static Singleton1 INSTANCE=new Singleton1();
/**
* 返回对象
* @return
*/
public Singleton1 getSigleton(){
return INSTANCE;
}
}
懒汉式-线程不安全(有缺陷,不可取)
package com.yadmire.design.ceational.singleton;
/**
* @className Singleton2
* @author: yadmire
* @create: 2022-05-15 16:24
* @Description: 懒汉式 (在第一次调用的时候实例化自己)
* 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
*/
public class Singleton2 {
//私有化构造,不允许新建对象
private Singleton2(){
}
private static Singleton2 INSTANCE;
/**
* 返回对象
* @return
*/
public Singleton2 getSigleton(){
// 首次加载时进行创建
if(INSTANCE==null){
INSTANCE=new Singleton2();
}
return INSTANCE;
}
}
懒汉式-线程安全
package com.yadmire.design.ceational.singleton;
/**
* @className Singleton3
* @author: yadmire
* @create: 2022-05-15 16:29
* @Description: 懒汉式-加锁,线程安全
*/
public class Singleton3 {
// 私有化构造
private Singleton3 (){}
private static Singleton3 INSTANCE=null;
// 加锁,保证单例,但是比较重,影响效率
public synchronized Singleton3 getSingleton3(){
if(INSTANCE==null){
INSTANCE=new Singleton3();
}
return INSTANCE;
}
}
懒汉式--双检锁
package com.yadmire.design.ceational.singleton;
/**
* @className Singleton4
* @author: yadmire
* @create: 2022-05-15 16:34
* @Description: 饿汉式-双重检查机制
*/
public class Singleton4 {
// 这里添加 volatile 保证创建过程是线程可见的
// new 操作在字节码层面上会分解成3个操作:
// 1. 给 singleton 分配内存
// 2. 调用 Singleton 的构造函数来初始化成员变量,形成实例
// 3. 将singleton对象指向分配的内存空间(执行完这步 singleton才是非 null了)
// 通过添加 volatile (内存屏障)保证线程的可见性与顺序性,同时保证不会发生指令重排,
// 但是此处保证的不是new 时的指令重排,而是保证在写操作时,通过内存屏障,为该内存区域加锁,保证不会有读操作,保证写期间不会有读该内存区域的操作
private static volatile Singleton4 INSTANCE= null;
private Singleton4(){}
public static Singleton4 getINSTANCE() {
if(INSTANCE == null){
// 局部加锁,增加效率
synchronized (Singleton4.class){
if(INSTANCE ==null){
INSTANCE=new Singleton4();
}
}
}
return INSTANCE;
}
}
枚举
枚举本身也是单例的,比如,如下的验证:
/**
* @className Singleton5
* @author: yadmire
* @create: 2022-05-15 16:58
* @Description: 额外:枚举类型
*/
public enum Singleton5 {
SINGLETON_5;
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
验证代码如下:
public class SingletonTest {
@Test
public void testSingletonEnum(){
System.out.printf("test for Singleton Enum");
Singleton5 singleton1 = Singleton5.SINGLETON_5;//枚举类型实例化(静态块中)
singleton1.setName("单例1");
System.out.println(singleton1.getName());
Singleton5 singleton2 = Singleton5.SINGLETON_5;
singleton2.setName("单例2");
System.out.println(singleton2.getName());
System.out.println(singleton1.getName() + ":" + singleton2.getName());
// 断言 两者是同一个对象
Assert.assertTrue(singleton1==singleton2);
}
}