何为单例模式
单例模式是针对一个类的设计方式而言,若该类使用了单例模式,那么再整个程序中这个类只会有一个对象,当创建第二个对象时就会直接报错。
即使只能创建一个对象,也会存在例外,即可以通过反射创建第二个实例,但这种情况也基本上不会见到。
如何保证只有一个对象
将该类的构造方法设置为private,之后只能在这个类中创建对象,若要在类外创建对象就会直接报错。
实现单例模式的两种方式以及保证线程安全
1.饿汉式
顾名思义,在这个类一开始,就创建出这个类的对象,代码如下:
java
class Singleton {
//实例
private static Singleton instance = new Singleton();
//私有的构造方法
private Singleton() {
}
//获取实例
public static Singleton getInstance() {
return instance;
}
}
当在类外创建对象时,就会直接报错:

当我们在类外获取多个实例时,这些实例均为同一个实例:
java
public class Demo4 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
//判断两个实例是否相同
System.out.println(singleton1 == singleton2);
}
}

在饿汉模式中,由于不涉及写操作,线程就是安全的。
2.懒汉式
顾名思义,在最后才创建出类的实例,甚至不创建实例,下面介绍最后创建实例的情况,代码如下:
java
class Singleton {
//先不初始化
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {//若实例未初始化就先初始化
instance = new Singleton();
}
return instance;
}
}
可是,上面的代码存在线程安全问题:
1)当线程1判断instance是否为null时,线程2也会判断instance是否为null,由于线程1还未将instance初始化,那么线程1和线程2都会对instance初始化,这样就会造成较大的资源开销,于是我们可以将if语句加锁,代码如下:
java
class Singleton {
//先不初始化
private static Singleton instance = null;
//创建锁对象
private static Object locker = new Object();
private Singleton() {
}
public static Singleton getInstance() {
synchronized (locker) {//加锁
if (instance == null) {//若实例未初始化就先初始化
instance = new Singleton();
}
}
return instance;
}
}
2)虽然这时instance的读写操作解决了线程安全的问题,但是由于一进入getInstance方法就会获取锁,导致其他的线程就会阻塞,即使instance已经初始化完成,还是会阻塞,这样就会降低代码执行效率;
我们可以在加锁之前先判断instance是否为null,只有当instance为null时才获取锁,改进代码如下:
java
class Singleton {
//先不初始化
private static Singleton instance = null;
//创建锁对象
private static Object locker = new Object();
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (locker) {
if (instance == null) {//若实例未初始化就先初始化
instance = new Singleton();
}
}
}
return instance;
}
}
3)由于instance = new Singleton()操作在CPU上对应多条指令,即操作不是原子的,这样就会引发指令重排序的线程安全问题,可以使用volatile关键字修饰instance以解决指令重排序问题,最终代码如下:
java
class Singleton {
//先不初始化
private static volatile Singleton instance = null;
//创建锁对象
private static Object locker = new Object();
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (locker) {
if (instance == null) {//若实例未初始化就先初始化
instance = new Singleton();
}
}
}
return instance;
}
}