Singleton(单例模式)
介绍
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
解决了 一个全局使用的类频繁地创建与销毁。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用
缺点:
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
饿汉模式(常用)
/**
* 饿汉模式 (常用)
* 以空间换时间 不管需不需要用到实例都要去创建实例
* 原理:类加载到内存后,就实例化一个单例,JVM保证线程安全
* 缺点:不管有没有用到,加载类的时候都会创建一个实例
*/
public class Mgr1 {
private final static Mgr1 INSTANCE = new Mgr1();
private Mgr1() {}
public void method() {}
public static Mgr1 getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Mgr1 m1 = Mgr1.getInstance();
Mgr1 m2 = Mgr1.getInstance();
System.out.println(m1==m2);
}
}
懒汉模式
/**
* 懒汉模式:
* 以时间换空间 只有在用到的时候才会创建一个实例
* 缺点: 线程不安全
*/
public class Mgr2 {
private static Mgr2 INSTANCE;
private Mgr2() {
}
public static Mgr2 getInstance() {
if (INSTANCE == null) {
try {
Thread.sleep(1000);
INSTANCE = new Mgr2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(() ->
System.out.println(Mgr2.getInstance().hashCode())
).start();
}
}
}
优化方案
双重判断
/**
* 优化方式
* 双重判断
* 通过 synchronized 解决, 但是效率会下降
*/
public class Mgr3 {
private static Mgr3 INSTANCE;
private Mgr3() {}
public static Mgr3 getInstance() {
if (INSTANCE == null) {
synchronized (Mgr3.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1000);
INSTANCE = new Mgr3();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(() ->
System.out.println(Mgr3.getInstance().hashCode())
).start();
}
}
}
静态内部类(懒加载)
/**
* 优化方式:
* 使用静态内部类
* JVM 会保证单例
* 加载外部类的时候不会加载内部类,这样就实现了懒加载
*/
public class Mgr4 {
private static class Mgr4Instance {
public final static Mgr4 INSTANCE = new Mgr4();
}
public static Mgr4 getInstance() {
return Mgr4Instance.INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(() ->
System.out.println(Mgr4.getInstance().hashCode())
).start();
}
}
}
枚举单例
/**
* 优化方式:枚举单例
* 不仅可以解决线程同步,还可以防止反序列化
*/
public enum Mgr5 {
/**
* 实例
*/
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(() ->
System.out.println(Mgr5.INSTANCE.hashCode())
).start();
}
}
}