单例模式适用场景
1、无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是他帮我们记录日志信息,除此之外,并不需要在它的实际对象上存储任何状态,这个时候我们就只需要一个实例对象即可
2、全局信息类:在一个类上记录网站访问次数,不希望有的访问被记录在对象A上,有的记录在对象B上,这个时候就让这个类成为单例
单例模式8种写法
饿汉式:在类加载时候,就把实例加载完毕了
懒汉式:单例在用到的时候才进行加载,可以节省内存
1、饿汉式(静态常量)(可用)
//描述 饿汉式(静态常量)
public class Singleton1 {
private final static Singleton1 INSTANCE = new Singleton1();
public Singleton1() {
}
public static Singleton1 getInstance(){
return INSTANCE;
}
}
2、饿汉式(静态代码块)(可用)
//描述 饿汉式(静态代码块)
public class Singleton2 {
private final static Singleton2 INSTANCE ;
static {
INSTANCE = new Singleton2();
}
public Singleton2() {
}
public static Singleton2 getInstance(){
return INSTANCE;
}
}
3、懒汉式(线程不安全)(不可用)
//描述 懒汉式(线程不安全)
public class Singleton3 {
private static Singleton3 instance;
private Singleton3() {
}
//两个线程同时进入if语句,可能创建出多个实例
public static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
4、懒汉式(线程安全)(不推荐)
//描述 懒汉式(线程安全)(不推荐)
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
//线程安全但是效率太低
public synchronized static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
5、懒汉式(线程不安全,同步代码块)[不可用]
//描述 懒汉式(线程不安全)(不推荐)
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (instance == null) {
//当两个线程同时进入if语句,就会创建多个实例
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
6、双重检查
//描述 双重检查(推荐面试使用)
public class Singleton6 {
private volatile static Singleton6 instance;
private Singleton6() {
}
public static Singleton6 getInstance() {
if (instance == null) {
//当两个线程同时进入if语句,二次判断是否为空,可以防止多次创建实例
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
优点:线程安全;延迟加载;效率较高
为什么要双重检查double-check
1、线程安全
2、单check不行,会导致创建多个实例
3、性能问题
为什么要用volatile
1、新建对象实际上有3个步骤
2、重排序会带来NPE:在第一个线程进行创建实例时,完成了第一步,但是内容还是空的,当第二个线程进入,判断实例非空,但是却没有内容,就会报空指针异常
3、防止重排序
7、静态内部类[推荐用]
当真正需要用到getInstance时,就会返回INSTANCE实例,实例才会进行初始化,并且由于JVM加载类的性质保证了即使多个线程同时访问这个对象也不会建造多个实例
//描述 静态内部类(推荐使用)
public class Singleton7 {
private Singleton7() {
}
private static class SingletonInstance {
//
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
8、枚举[推荐用]
//描述 枚举单例(推荐使用)
public enum Singleton8 {
INSTANCE;
public void whatever(){
}
}
使用枚举单例
Singleton8.INSTANCE.whatever();