一、概念
单例模式(Singleton Pattern)是 Java 中较为简单的设计模式之一。该模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类需要提供访问唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
梳理单例模式的3个重点:
1、类所对应的实例只能有一个。
2、类的构造方法不能被外界调用。
3、需要为外界提供一个获取该类唯一实例的方法。
二、饿汉模式实现单例
/**
* 饿汉式的特点:
* 1.线程安全
* 2.没有锁,效率高
* 3.缺点是容易产生垃圾对象,因为该类被加载就会被实例化,就算没有调用getInstance()也会产生对象
*/
public class Singleton {
//创建该类的唯一实例
private static Singleton instance = new Singleton();
//私有化构造器,不让外界创建对象
private Singleton (){}
//为外界提供一个静态的公开方法,用于获取该类的实例
public static Singleton getInstance() {
return instance;
}
}
叫饿汉模式的原因是:就算不调用getInstance()
获取实例,也可能会创建类的实例。
三、懒汉模式实现单例
顾名思义:调用的时候才创建,不调用不创建。
1.懒汉式,线程不安全
//方式1:
public class Singleton {
//为防止类加载时就创建实例,这里仅声明不赋值,在调用的时候再进行赋值
private static Singleton instance;
//私有化构造器防止被外界调用
private Singleton (){}
//为外界提供获取实例的方法
public static Singleton getInstance() {
instance = new Singleton(); //调用时再赋值
return instance;
}
}
//分析:很明显getInstance()方法是有问题的,因为每调用一次就会创建新的实例并返回,
//无法保证单例,所以需要对getInstance()进行修改。
//方式2:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
//增加判断:instance等于null时创建对象返回,不等于null表示对象已经存在则直接返回
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
//分析:这个getInstance()就可以了吗?
//答案:不可以,因为在多线程下运行还是会有问题。所以我们来解决一下。
2.懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
//为方法加锁synchronized,这样就能保证该方法一个线程执行完毕,另一个才可以执行
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//分析:虽然达到了线程安全的目的,但是锁范围有点大,效率较低。
再进行修改,优化一下效率:
/**
* (双检锁/双重校验锁)实现懒汉单例,保证线程安全。
* 核心在于:缩小锁的范围,同时增加两次判断,线程安全的同时保证了效率。
*/
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
四、静态内部类实现单例
/**
* 这种方式线程安全的同时,还实现懒加载。
* 这种方式是 Singleton 类被装载了,instance 不一定被初始化。
* 因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,
* 才会显式装载 SingletonHolder 类,从而实例化 instance。
*/
public class Singleton {
//在内部类中创建该类的实例
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//私有化构造器
private Singleton (){}
//提供获取实例的方法
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
到这里,我们介绍了几种用于实现单例的方案,感觉上还可以,但是要注意Java的反射机制会打破这一切,因为我们实现单例的思路是私有化构造器,不然外界调用,但是反射就可以调用到私有构造。
五、枚举实现单例
/**
* 枚举实现单例
* 1.枚举自带私有无参构造,这恰好满足我们的需求,防止外部调用
* 2.枚举是不允许反射去调用构造器的,反射在通过newInstance创建对象时,
* 会检查该类是否enum修饰,如果是则抛出异常,反射失败。
*/
public enum Singleton {
INSTANCE;
}
点滴积累,只为天天向上