单例模式,就是一个类中只有一个实例。主要有懒汉模式和饿汉模式。

饿汉模式是在类加载的同时就创建实例,而懒汉模式是使用时才创建实例。

饿汉模式

1
2
3
4
5
6
7
8
9
10
class Singleton{
//1.使用static创建一个实例,并且立即实例化
private static Singleton instance = new Singleton();
//2.为了防止在其他地方不小心new这个Singleton,把方法构造为private
private Singleton(){}
//3.提供一个方法,让外面能够拿到唯一实例
public static Singleton getInstance() {
return instance;
}
}

在多线程中,多个线程对同一数据进行读操作,线程安全。

懒汉模式

1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton{
//1.不立即初始化实例,使用时再初始化
private static Singleton1 instance = null;
//2.将构造方法设为private
private Singleton1(){}
//3.提供方法获取实例
public static Singleton1 getInstance() {
if(instance==null){
instance = new Singleton();
}
return instance;
}
}

多线程中,多个线程对同一对象进行读、写操作,线程不安全。线程不安全出现在首次创建实例时,多个线程同时调用getInstance方法,可能创造出多个实例。

改进1:

对getInstance方法加锁,保证操作的原子性,实现线程安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Singleton{
//1.不立即初始化实例,使用时再初始化
private static Singleton instance = null;
//2.将构造方法设为private
private Singleton1(){}
//3.提供方法获取实例
public static synchronized Singleton getInstance() {
if(instance==null){
instance = new Singleton();
}
return instance;
}
}

但这种方法性能较差,毕竟线程不安全只出现在首次创建实例时,为getInstance方法加锁后每次都要加锁解锁,所以做如下改进。

改进2:

进入getInstance方法后首先判断instance是否为空,即是否为第一次创建实例,是才需要加锁,否则不需要加锁。同时为了避免 “内存可见性” 导致读取的 instance 出现偏差, 因此为instance加上volatile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton{
//1.不立即初始化实例,使用时再初始化
private static volatile Singleton1 instance = null;
//2.将构造方法设为private
private Singleton(){}
//3.提供方法获取实例
public static Singleton getInstance() {
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}