单例模式
单例-保证一个类的具体的实例在整个应用程序中只有唯一的一个
- 构造私有化
- 提供这个类的唯一(静态)实例
- 提供一个公开的静态方法来返回这个实例
1. 饿汉式
单线程/多线程场景下都是安全的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package tech.aistar.design.Singleton.v1;
public class Singleton { private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() { return singleton; } }
class SingletonTest { public static void main(String[] args) { Singleton singleton = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton2 == singleton); } }
|
2. 懒汉式
单线程下是ok的,多线程下是不ok的 无法在多线程下保证这个实例是唯一的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package tech.aistar.design.Singleton.v2;
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() { return singleton == null ? singleton = new Singleton() : singleton; } }
class SingletonTest { public static void main(String[] args) { Singleton singleton = Singleton.getSingleton(); Singleton singleton2 = Singleton.getSingleton(); System.out.println(singleton2 == singleton); } }
|
3. 剖析线程不安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| package tech.aistar.design.Singleton.v2;
public class Singleton {
private static Singleton instance;
private Singleton() { System.out.println("构造块"); }
public static Singleton getInstance() { return instance == null ? instance = new Singleton() : instance; } } class SingletonTest { public static void main(String[] args) { for (int i = 0; i < 100; i++) { Thread t = new Thread(() -> { Singleton s1 = Singleton.getInstance(); }); t.start(); } } }
|
1 2 3 4 5 6 7
| 构造块 构造块 构造块 构造块 构造块 构造块 构造块
|
4. 懒汉式-线程安全
使用同步锁– synchronized
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package tech.aistar.design.Singleton.v2;
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static synchronized Singleton getSingleton() { return singleton == null ? singleton = new Singleton() : singleton; } }
class SingletonTest { public static void main(String[] args) { Singleton singleton = Singleton.getSingleton(); Singleton singleton2 = Singleton.getSingleton(); System.out.println(singleton2 == singleton); } }
|
5. 饿汉式/懒汉式的区别
线程安全性方面
饿汉模式 - 单线程/多线程 - 都是线程安全的 - 因为类加载的时候(线程对象还不存在!),就会初始化那个单例的实例.
懒汉模式 - 需要通过加锁的方式,来实现多线程安全.
效率方面 - 懒汉模式通常是需要加锁的,所以涉及到频繁加锁和释放锁的动作,而”锁”也是一个比较昂贵的资源对象[占内存]
效率不如饿汉模式.
占内存方面 - 饿汉模式比较占内存 - 类加载的时候,它在内存中就存在了. 懒汉模式是你需要使用它实例的时候,它才会第一次实例化.
6. 双重检测锁-笔试/面试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package tech.aistar.design.Singleton.v5;
public class Singleton {
private static volatile Singleton instance;
private Singleton() { System.out.println("构造块"); }
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); } } } return instance; } }
class SingletonTest { public static void main(String[] args) {
for (int i = 0; i < 100; i++) { Thread t = new Thread(() -> { Singleton s1 = Singleton.getInstance(); }); t.start(); } } }
|