单例模式
单例模式
单例模式的理解
单例模式,字面上理解当是使用过程中,只有一个实例存在于系统中
联系实际生活中的例子:
- 每个人只有一个身份证
- 同一个宇宙 只有一个太阳 一个地球 一个中国
- 每个人的指纹都是唯一的(不包括克隆人的情况)
电脑世界中的例子
- 一个电脑对应一个mac地址
- 一个时刻一个时间戳
- 数据库表中的主键唯一对应一条记录
- Spring IOC容器中的对象是单例的
如果这个系统中只有一份的单例,那么则相安无事,假如这个系统又加上例外的实例,也许就会像电影世界中说的,平行宇宙中出现俩个你,也许这个世界就会发生崩塌,所以有些东西只能有一份,超过一份,这个世界甚至会易燃易爆炸。
Java中单例模式
创建关键点:
- 限制创建 Java中是在构造器中加上private
- 自己创建实例 也就是创建者只能是自己 持有自身对象
- 需要一个返回该单例对象的静态static方法
饿汉式
关键点:
- 构造器私有(限制只能在该类中进行实例化)
- 在加载类的时候就进行初始化
- 定义一个获取单例的方法
Good:
- 简单明了
Bad:
- 如果创建的过程很繁琐,那么就有可能需要时间比较多,没有使用的话,会浪费这部分资源
Code:
/**
* 单例模式
* @author 洪晓鸿
* @date 2019年4月16日 下午1:53:14
* @version V1.0
*/
public class HungrySingleton {
// 创建一个引用用于保存单个的实例 这里使用static保证只有一个引用
private static HungrySingleton instance = new HungrySingleton();
// 将构造器私有化 只能在该类中创建实例/对象
private HungrySingleton() {
super();
}
// 饿汉式单例模式 对象创建的时候就初始化
// 必须使用static 如果不使用static 则没有办法使用
public static HungrySingleton getSingleton() {
return instance;
}
}
懒汉式
关键点:
- 构造器私有(限制只能在该类中进行实例化)
- 在调用方法的时候就进行初始化
- 定义一个获取单例的方法
Good:
- 简单明了
- 节省资源
Bad:
- 并发情况下有可能出现创建多个实例的情况
Code:
/**
* 单例模式
* @author 洪晓鸿
* @date 2019年4月16日 下午1:53:14
* @version V1.0
*/
public class Singleton {
// 创建一个引用用于保存单个的实例 这里使用static保证只有一个引用
private static Singleton instance;
// 将构造器私有化 只能在该类中创建实例/对象
private Singleton() {
super();
}
// 懒汉式单例模式 也就是当要用到的时候才进行初始化
// 必须使用static 如果不使用static 则没有办法使用
public static Singleton getSingleton() {
// 如果为空 则进行初始化
if (instance==null) {
instance = new Singleton();
}
return instance;
}
}
线程安全懒汉式
关键点:
- 构造器私有(限制只能在该类中进行实例化)
- 在调用方法的时候就进行初始化
- 在方法前面使用同步synchronized
- 定义一个获取单例的方法
Good:
- 使用同步保证只有一个实例被创建
- 到使用时才进行初始化,节省部分资源
Bad:
- 因为直接在方法上加了synchronized关键字 同一时刻只能有一个访问 无法实现多个同时访问
Code:
/**
* 单例模式--线程安全式 效率比较低 如果多个进程访问 必须先等上一个线程访问完才能访问
* @author 洪晓鸿
* @date 2019年4月16日 下午5:28:38
* @version V1.0
*/
public class SynchronizationSingleton {
// 创建一个引用用于保存单个的实例 这里使用static保证只有一个引用
// volatile保证 instance 在所有线程中同步
private volatile static SynchronizationSingleton instance;
// 将构造器私有化 只能在该类中创建实例/对象
private SynchronizationSingleton() {
super();
}
// 懒汉式单例模式 也就是当要用到的时候才进行初始化
// 必须使用static 如果不使用static 则没有办法使用
public synchronized static SynchronizationSingleton getSingleton() {
// 如果为空 则进行初始化
if (instance==null) {
instance = new SynchronizationSingleton();
}
return instance;
}
}
双重判断线程安全懒汉式
关键点:
- 构造器私有(限制只能在该类中进行实例化)
- 在调用方法的时候就进行初始化
- 在方法内使用同步synchronized
- 进入方法后先进行判断对象是否为null 再在同步代码块中判断对象是否为null
- 定义一个获取单例的方法
Good:
- 使用同步保证只有一个实例被创建
- 到使用时才进行初始化,节省部分资源
- 因为在方法内加上了双重判断,使得可以同一时间多次调用该方法
Bad:
- 效率自然是比不上不同步的,好与坏其实很难讲,主要看场景吧,如果没在并发情况下,自然可以使用非线程安全的
Code:
/**
* 单例模式--双重线程安全式
* @author 洪晓鸿
* @date 2019年4月16日 下午5:28:38
* @version V1.0
*/
public class DoubleSynchronizationSingleton {
// 创建一个引用用于保存单个的实例 这里使用static保证只有一个引用
private static DoubleSynchronizationSingleton instance;
// 将构造器私有化 只能在该类中创建实例/对象
private DoubleSynchronizationSingleton() {
super();
}
// 懒汉式单例模式 也就是当要用到的时候才进行初始化
// 必须使用static 如果不使用static 则没有办法使用
public static DoubleSynchronizationSingleton getSingleton() {
// 如果为空 才进入同步
if (instance==null) {
synchronized(DoubleSynchronizationSingleton.class) {
// 取得锁之后再次判断是否已经初始化 没有则进行初始化
if (instance==null) {
// 初始化
instance = new DoubleSynchronizationSingleton();
}
}
}
return instance;
}
}
静态内部类式
关键点:
- 构造器私有(限制只能在该类中进行实例化)
- 在调用方法的时候就进行初始化
- 静态内部类持有单例对象 并且为static 保证只有一次初始化
Good:
- 使用静态内部类 可以将类的初始化延迟到调用方法的一刻 节约资源
Bad:
- 想无
Code:
/**
* 通过静态内部类实现单例
* @author 洪晓鸿
* @date 2019年12月6日 下午10:10:23
* @version V1.0
*/
public class StaticInnerSingleton {
/**
* 私有化构造器
*/
private StaticInnerSingleton() {
System.out.println("单例模式初始化");
}
/**
* 获取单例对象
* @return
*/
public static StaticInnerSingleton getSingleton() {
return Inner.singleton;
}
/**
* 通过静态内部类加载
* @author 洪晓鸿
* @date 2019年12月6日 下午10:17:00
* @version V1.0
*/
private static class Inner {
private static final StaticInnerSingleton singleton = new StaticInnerSingleton();
}
/**
* 测试一下
* @param args
*/
public static void main(String[] args) {
StaticInnerSingleton s0 = getSingleton();
StaticInnerSingleton s1 = getSingleton();
if (s0==s1) {
System.out.println("Equals");
} else {
System.out.println("Not Equals");
}
}
}
枚举式-终极奥义
关键点:
- 如果需要某些属性,可以在枚举类中定义
- 使用的时候直接使用类名加上.再加上对象就可以使用
Good:
- 使用方便 不存在线程问题
Bad:
- 暂时没想到
Code:
/**
* 使用枚举类型创建一个单例
* @author 洪晓鸿
* @date 2019年5月6日 下午4:28:22
* @version V1.0
*/
public enum SimgletonEnum {
/**
* 需要使用自定义的构造器对其进行初始化
* 用 , 隔开不同的枚举
* 用 ; 隔开枚举类型与 枚举类型的属性和方法
*/
INSTANCE(1),;
/**
* 一下的操作跟普通类一样
*/
private int key;
SimgletonEnum(int key) {
// 不可以加上修饰符 访问控制修饰符也不行呢
this.key = key;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
}