一、单例模式是什么?
单例模式(Singleton Pattern)是最简单的设计模式之一.
单例模式,顾名思义就是只有一个实例,并且自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
二、单例模式的几种实现方式
1.懒汉式 线程不安全
代码如下(示例):
/**
* 懒汉模式
* 只有用到的时候才会创建,用到的时候才会检测是否已经实例化,如果有则返回,没有则进行初始化实例.
*/
public class Idler {
private static Idler instance;
private Idler() {
}
public static Idler getInstance() {
if (instance == null) {
instance = new Idler();
}
return instance;
}
}
优点:
1、在获取实例的方法中,进行实例的初始化,节省系统资源
缺点:
1、每次获取实例都要进行非空检查,系统开销大
2、非线程安全,当多个线程同时访问getInstance()时,可能会产生多个实例
2. 懒汉式 线程安全
代码如下(示例):
/**
* 懒汉模式
* 只有用到的时候才会创建,用到的时候才会检测是否已经实例化,如果有则返回,没有则进行初始化实例.
* 此锁每次调用 getInstance()方法都要加锁 效率极低
*/
public class Idler {
private static Idler instance;
private Idler() {
}
public static synchronized Idler getInstance() {
if (instance == null) {
instance = new Idler();
}
return instance;
}
}
优点:
1、在获取实例的方法中,进行实例的初始化,节省系统资源
2、线程安全但影响效率
缺点:
1、必须加锁 synchronized 才能保证单例,但加锁会影响效率。
3.饿汉式
代码如下(示例):
/**
* 饿汉模式
* 实例在类初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。
*/
public class Hungry {
private static Hungry instance = new Hungry();
private Hungry() {
}
public static Hungry getInstance() {
return instance;
}
}
优点:
1、线程安全
2、获取实例速度快
缺点:
1、类加载即初始化实例
2、内存浪费
4.双检索(双重检索模式)
代码如下(示例):
/**
*双检锁(双重检查锁)
* synchronized 关键字 没有实例化的时候才会进行加锁.比懒汉式 加方法锁 效率要高.
* volatile(必加)
*
* 当A/B 两线程同时执行getInstance方法时,A先进入后会加锁,B则等待.
* 此时A将instance实例化后会同步到主线程,A执行完后会.B会向主线程拷贝instance实例,
* 但此时实例已被A修改.此时instance!=null,拿到后也不会在进行新建实例
*/
public class DoubleCheck {
//volatile 必加 保证可见性和禁止指令重排序
private volatile static DoubleCheck instance;
private DoubleCheck() {
}
public static DoubleCheck getInstance() {
if (instance == null) {
synchronized (DoubleCheck.class) {
if(instance==null){
instance = new DoubleCheck();
}
}
}
return instance;
}
}
双检索:安全且在多线程情况下能保持高性能,但略复杂。
volatile关键字:
读:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。
写: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。