目录
一、定义及特点
Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
PS:记录于2022年6月27日
经过复习,果然温故而知新~
让我对单例模式有了更深一步的理解,更新在这篇文章之下,希望记录并对大家有用~
二、分别介绍
2.1 饿汉式
1)优点:线程安全
2)缺点:无法对instance实例做延时加载
3)优化:懒汉式
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
2.2 懒汉式
1)缺点:线程不安全,在多线程下无法保证实例唯一
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
解决办法:
1-1 加同步锁
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
1-2 双重检查锁定
1-3 静态内部类
2.3 双重检查锁定(DCL)
1)缺点:JVM的即时编译器中存在指令重排序的优化
2)优化:静态内部类/枚举
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
2.4 静态内部类(推荐使用)
public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。
2.5 登记式
登记式单例
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(), single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
if(name == null) {
name = Singleton3.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
2.6 枚举
经洪洋大神的指点之后,在后面增加一种单例的方式。
使用java的枚举
public enum SingletonEnum
{
INSTANCE;
}
--------------------------------华丽丽的分割线------------------------------------------
三、懒汉和饿汉的区别
懒汉式和饿汉式的区别
饿汉式是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
A: 线程安全
饿汉式 -- 安全
懒汉式--不安全 需要加锁
B: 资源加载和性能
饿汉式 -- 类加载把单例初始化,会占用一些内存。第一次调用时候会快。
懒汉式 -- 第一次调用时候需要初始化,性能上有些延迟。
--------------------------------华丽丽的分割线------------------------------------------
四、总结
综合以上情况,常见的和不常见的都总结在上面了。
原以为单例模式只是一个孤零零的设计模式去理解,这样只是死记硬背。
在应用中发现并深层次加深理解,然后再反回来思考,能些许发现其中的奥妙。
甚至会感叹前人栽树后人乘凉,伟大的思想不断继承和引用到了现在,着实奇妙~