单例模式(SingLeton)
什么是单例模式
**单例模式(SingLeton)**是一种经典的设计模式(大佬们总结出来的代码编写套路)
单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例, 防止频繁地创建对象使得内存飙升,让所有需要调用的地方都共享这一单例对象。
1. 饿汉模式(安全)
在类加载时已经创建好该单例对象,等待被程序使用
(天然安全的,仅只是读取了变量的内容)
class Singleton {
//私有化,只能在类内部创建对象
private static Singleton instance = new Singleton();
//向外提供一个公共的静态方法,用于外部引用该对象
public static Singleton getInstance() {
return instance;
}
//禁止外部 new 实例
private Singleton(){};
}
public class TestDemo1 {
public static void main(String[] args) {
//此时S1 与 S2 是同一个对象
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//Singleton s3 = new Singleton();//报错
}
}
2. 懒汉模式(危险)
先不创建,在真正需要使用对象时才去创建该单例类对象
代码演示:
public class Singleton {
// 提供一个对象的引用
private static Singleton instance = null;
// 防止外部new对象
private Singleton(){}
// 用于创建对象或使得外部能够访问到创建的对象
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
(不安全,有读也有写,并发环境下很可能出现多个Singleton实例):
如:两个线程t1,t2同时进入该方法,需要返回给 t1 ,t2 对象实例。当 t1 进入该方法后,进行判断,不为 null,因此需要创建一个对象,而在创建对象的过程中,此时t2 刚好也进入了该方法,也被判断成了不为 null,此时,t1, t2就指向不同的对象了,所以它是线程不安全的。
解决办法
- 加锁,在线程1工作时避免线程2进入创建对象
class SingletonLazy {
private static SingletonLazy instance = null;
public synchronized static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
但加锁会使程序堵塞,运行速度变慢
- 双重 if ,判断 instance 是否被初始化完成
class SingletonLazy {
private static SingletonLazy instance = null;
public static SingletonLazy getInstance() {
if(instance == null ){
synchronized(SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){}
}
public class TestDemo2 {
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 = s2);
}
}
- 再使用 volatile 禁止指令重排序,防止编译器优化,保证后续线程肯定拿到是完整对象
private static volatile SingletonLazy instance = null;