单例模式介绍
- 保证类全局只有一个实例,并且提供一个访问该实例的全局访问函数
- 因此需要将构造器私有化,防止外部new该类的对象
- 本文介绍五种单例模式并且使用Java语言实现
1、饿汉式
package com.ly.singleton;
/**
* 测试饿汉式单例
* liyang 2020-07-07
* 步骤:1、私有化构造器
* 2、初始化类就构造静态对象
* 3、提供静态对象的外部获取方法(静态)
*
* 优点:线程安全,因为类加载过程是天然线程安全的,因此不需要加synchronized,从而运行时效率高
*
* 缺点:饿汉式类加载过程立刻加载出对象实例,因此没有延时加载优势
* 如果只是加载类本身,而不需要调用getSingletonInstance(),甚至永远没有调用,则会造成资源浪费!
*/
public class SingletonDemo01 {
public static void main(String[] args) {
Singleton01 s1 = Singleton01.getSingletonInstance();
Singleton01 s2 = Singleton01.getSingletonInstance();
System.out.println(s1 == s2);
}
}
class Singleton01 {
//类初始化时,立刻加载类的这个对象,类加载过程是天然线程安全的
private static final Singleton01 instance = new Singleton01();
//私有化构造器
private Singleton01(){}
// 方法没有同步,效率高
public static Singleton01 getSingletonInstance(){
return instance;
}
}
true
Process finished with exit code 0
2、懒汉式
package com.ly.singleton;
/**
* 单例模式的懒汉式实现(添加synchronized)
* liyang 2020-07-07
*/
public class SingletonDemo02 {
public static void main(String[] args) {
Singleton02 s1 = Singleton02.getSingletonInstance();
Singleton02 s2 = Singleton02.getSingletonInstance();
System.out.println(s1 == s2);
}
}
class Singleton02 {
//类初始化时,不加载类的这个对象(懒加载, lazy load)
private static /*final*/ Singleton02 instance = null;
//私有化构造器
private Singleton02(){}
// 方法同步,效率低
public static synchronized Singleton02 getSingletonInstance(){
if(instance == null) instance = new Singleton02();
return instance;
}
}
true
Process finished with exit code 0
3、双重检测
package com.ly.singleton;
/**
* 双重检测锁实现
* liyang 2020-07-08
*/
public class SingletonDemo03 {
public static void main(String[] args) {
Singleton03 s1 = Singleton03.getSingleton();
Singleton03 s2 = Singleton03.getSingleton();
System.out.println(s1);
System.out.println(s2);
}
}
class Singleton03 {
private volatile static Singleton03 singleton;
private Singleton03 (){}
public static Singleton03 getSingleton() {
if (singleton == null) {
synchronized (Singleton03.class) {
if (singleton == null) {
singleton = new Singleton03();
}
}
}
return singleton;
}
}
com.ly.singleton.Singleton@1b6d3586
com.ly.singleton.Singleton@1b6d3586
Process finished with exit code 0
说明:详细见双重加载详细介绍 + 深入理解Java中的volatile关键字
4、静态内部类实现方法
package com.ly.singleton;
/**
* 静态内部类实现方式
* liyang 2020-07-08
* 优点:线程安全、调用效率高,懒加载
*
* 说明:外部类中没有static属性,不会像饿汉式那样立刻加载对象
* 只有真正调用getInstance()方法,才会加载静态内部类。
* 加载时线程是安全的
* instance是static *final* 类型,保证了内存中只有一个实例
* 兼并了并发高效调用和延迟加载优势
*/
public class SingletonDemo04 {
public static void main(String[] args) {
Singleton04 s1 = Singleton04.getSingletonInstance();
Singleton04 s2 = Singleton04.getSingletonInstance();
System.out.println(s1);
System.out.println(s2);
}
}
class Singleton04 {
//构造器私有化
private Singleton04(){}
//静态内部类
private static class Anonymous{
private static final Singleton04 instance = new Singleton04(); //final可不加
}
//获取对象实例
public static Singleton04 getSingletonInstance(){
return Anonymous.instance;
}
}
com.ly.singleton.Singleton04@1b6d3586
com.ly.singleton.Singleton04@1b6d3586
Process finished with exit code 0
5、枚举实现
package com.ly.singleton;
/**
* 使用枚举实现单例模式
* liyang 2020-07-08
*
* 优点:实现简单
* 枚举本身就是单例,有JVM从根本上提供保障,避免了通过反射和反序列化调用私有构造器的漏洞!
* 缺点:没有延迟加载
*
*/
public class SingletonDemo05 {
public static void main(String[] args) {
Singleton05 s1 = Singleton05.getSingletonInstance();
Singleton05 s2 = Singleton05.INSTANCE;
System.out.println(s1);
System.out.println(s1.str);
System.out.println(s1 == s2);
}
}
enum Singleton05 {
//这个枚举本身就是到单例对象
INSTANCE;
//获取这个实例对象
public static Singleton05 getSingletonInstance() {
return INSTANCE;
}
//可以添加自己需要的操作
String str = "hello world";
}
INSTANCE
hello world
true
Process finished with exit code 0
总结:
主要 |
|
其他 |
|
如何选用 |
|
参考资料:
https://www.runoob.com/design-pattern/singleton-pattern.html