单例模式
核心 :
- 构造方法私有化
- 全局静态对象
- 一个类只能创建一个对象
由懒汉式
和饿汉式
两种加载方式
- 类加载好的时候就会创建对象(懒汉式)
- 直到使用的时候才创建(饿汉式)
什么时候用单例
- 一个类只要一个对象的时候
- 视频播放器,音频播放器
- 数据库的连接
- 设置信息
开发中一般使用饿汉式,面试的时候使用懒汉式
单例设计模式需要考虑哪些问题
- 这个对象谁来创建? 自己来创建
- 这个对象放在那里? 自己使用成员变量来保存
- 如何给别人用呢? 提供公共的访问方式
java中的单例写法
class VideoPlay { //普通单例模式 懒汉
private static VideoPlay ins = new VideoPlay();
private VideoPlay() { }
public synchronized static VideoPlay getIns() {
return VideoPlay.ins;
}
}
饿汉式 双重锁(参考博客https://www.jianshu.com/p/b0e527336a72)
- (
JDK1.5后
)使用volatile
关键字修饰静态
变量ins
volatile
可以避免指令重排,每一条线程取变量值都是从主储存
里取,保证了变量是最新修改的- 指令重排导致的问题
Test c = new Test()
理论过程为,1.分配内存 2.初始化 3.返回内存地址
Test c = new Test()
实际过程为,1.分配内存 2.返回地址 3. 初始化- 当
线程 A
执行 1.分配内存 2.返回地址 (还未初始化)
线程 B
发现ins不为空(还未初始化), 就直接拿来用 ,所以造成了不安全
class VideoPlay2 { //双重锁 JDK1.5写法
/**
使用 volatile 关键字 每次都从主储存取值(每次都能取到最新的值)
如果不加 volatile 关键字 jvm会对其进行指令重排
new VideoPlay2() 过程为
理论过程 :1.分配内存 2.初始化 3.返回内存地址
实际底层回进行重排过程 :1.分配内存 2.返回地址 3. 初始化
由于重排,
线程 A 执行1.分配内存 2.返回地址(还未初始化)
线程 B 发现ins不为空(还未初始化), 就直接拿来用 ,所以造成了不安全
*/
private volatile static VideoPlay2 ins;
public static VideoPlay2 getIns() {
if(ins == null) {
synchronized (VideoPlay2.class) {
if(ins == null) ins = new VideoPlay2();
}
}
return ins;
}
}
或则使用静态内部类
实现 饿汉式 双重锁
class VideoPlay3 { //双重锁 使用静态内部类实现
private static class Helper {
private static VideoPlay3 ins = new VideoPlay3();
}
private VideoPlay3() { }
public static final VideoPlay3 getIns() {
return Helper.ins;
}
}