单例模式实现

懒汉式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
public class Lazy {
private static Lazy lazyInstance;

private Lazy(){}

public static Lazy getLazyInstance(){
if(lazyInstance == null){
lazyInstance = new Lazy();
}
return lazyInstance;
}
}

懒汉式是指先不创建实例,当首次调用后再创建。

私有化构造方法,不能使用 new 进行实例化,使用方法判断当前实例是否已存在,不存在则创建,保证实例的单一性。

线程不安全,在多线程情况下,可能首次会有多个线程一起调用 getLazyInstance方法 ,那么此时实例为空,会同时创建多个实例。

饿汉式(线程安全)

1
2
3
4
5
6
7
8
9
public class Hungry {
private static Hungry hungryInstance = new Hungry();

private Hungry(){}

public static Hungry getHungryInstance(){
return hungryInstance;
}
}

饿汉式指无论是否要使用实例,先进行一次实例化,随后需要实例时直接调用方法返回即可;

线程安全,因为初始化类时已经进行了实例化。但若长时间不使用该实例会造成资源浪费。

懒汉式(线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
public class LazyConcurrent {
private static LazyConcurrent lazyInstance;

private LazyConcurrent(){}

public static synchronized LazyConcurrent getLazyInstance(){
if(lazyInstance == null){
lazyInstance = new LazyConcurrent();
}
return lazyInstance;
}
}

在实例化执行方法上加锁保证线程安全。

线程安全,但效率低,多线程争夺锁会造成线程阻塞。

双重检查锁实现(线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DoubleCheck {
private static volatile DoubleCheck doubleCheckInstance;

private DoubleCheck(){}

public static DoubleCheck getInstance(){
if(doubleCheckInstance == null){
synchronized (DoubleCheck.class){
if(doubleCheckInstance == null){
doubleCheckInstance = new DoubleCheck();
}
}
}
return doubleCheckInstance;
}
}

双重检查锁相当于是线程安全懒汉式的优化版。

首先我们不是在执行方法上加锁,而是在方法内部加锁。先进行一次实例判断,如果为空则获得锁然后再进行一次判断,因为第一次判断可能有多个线程同时通过,而获取锁后需要第二次判断再实例对象,以防同时通过第一次判断线程进行多次实例化。

实例变量使用volatile修饰。我们正常代码实例化执行步骤如下:

1
2
3
4
doubleCheckInstance = new DoubleCheck();
1、为对象分配内存空间
2、初始化对象
3、将对象指向分配好的内存空间

由于JVM的指令重排机制,指令执行顺序可能会由原来的123变为132。而在多线程的影响下,会导致线程得到一个尚未初始化的实例。为了解决该问题可以在声明实例变量时加上 volatile,禁止JVM进行指令重排,保证多线程的安全。

静态内部类实现(线程安全)

1
2
3
4
5
6
7
8
9
10
11
public class Single {
private Single(){}

private static class SingleInner{
private static final Single instance = new Single();
}

public static Single getInstance(){
return SingleInner.instance;
}
}

当外部类Single加载时,其静态内部类SingleInner并未加载,当调用实例获取方法走到返回值时,才会去加载静态内部类,并进行实例化。

使用静态内部类可以延迟实例化,节省资源,并保证线程安全。

枚举实现(线程安全)

1
2
3
4
5
6
public enum Unique {
INTANCE;

public void doSomething(){
}
}

枚举默认即线程安全 + 单例,还可防范一系列反射破解单例的操作。

设计模式七大原则

  • 单一职责原则:一个类只负责一个功能,降低耦合性,方便迭代维护。
  • 开放封闭原则:类、方法可以进行功能扩展,但不能修改。对扩展开放,对修改封闭。
  • 依赖倒置原则:高级模块不能依赖低级模块,都应该依赖于接口。先将类进行抽象,先设计功能接口,再对接口进行功能细节的实现。
  • 接口隔离原则:不同接口定义不同的功能。
  • 里氏代换原则:子类可替换其父类。
  • 迪米特原则:每个模块间要尽可能少的相互调用,减少依赖,降低耦合性。