1、单例模式(Singleton Pattern)
单例模式是一种常见的设计模式,目的是确保一个类只会存在一个实例,并且该类提供全局进行访问此实例的方法。
2、懒汉式
/** * @author Hao * 单例模式:懒汉式 * 懒汉式是线程不安全的,懒汉式在多线程环境下,可能会出现多个实例,所以需要加锁。 * 懒汉式可以延迟加载,不会在首次加载时就初始化,而是在调用时才创建。 * * 总结: * 优点:延迟加载,减少内存开销。 * 缺点:在多线程环境下,可能会出现多个实例。 */
2.1、方法一:双重检查锁
// 方法一:双重检查锁 // -------------------------方法一开始------------------------- // 私有化定义实例 private static volatile SingletonLazy instance; /** * 提供公共获取实例方法 * @return SingletonLazy 实例 * 使用双重判断加锁,而不直接于方法上加锁,是为了避免每次调用getInstance()方法时, 都进行加锁,造成不必要的开销。 */ public static SingletonLazy getInstance(){ // 第一次判断,判断是否已经实例化,如果已经实例化,那么就不会被多线程影响 出现创建多个实例的情况,所以直接返回 if (instance == null){ // 如果没有实例化,那么就加锁,避免多线程下,多个线程同时创建实例的情况 synchronized (SingletonLazy.class){ // 此时进行第二次判断,避免多个线程同时访问抢锁,导致创建多个实例。举个例子 // 线程a、b同时访问,此时instance还未实例化。那么此时线程a、b都会通 过第一次判断。 // 这时线程a率先强到锁,如果不进行二次判断,线程a创建实例后,线程b获得锁 也会创建实例,从而造成多个实例。 if (instance == null){ instance = new SingletonLazy(); } } } return instance; } // -------------------------方法一结束-------------------------
2.2、方法二: 静态内部类
// 方法二:静态内部类 // -------------------------方法二开始------------------------- // 创建私有内部类,提供实例 private static class SingletonHolder{ private static final SingletonLazy instance = new SingletonLazy(); } /** * 提供公共获取实例方法 * @return SingletonLazy 实例 * * 使用静态内部类,避免了线程不安全,延迟加载,效率更高。 * 静态内部类在第一次使用的时候才会被加载,不会造成内存浪费。 */ public static SingletonLazy getSingleton(){ return SingletonHolder.instance; } // -------------------------方法二结束-------------------------
2.3、方法三:枚举类
/** * @author Hao * * 通过枚举方式创建 */ public enum Singleton { // 没错就是这么简单 =.=,而且枚举的实例化是线程安全的、不会被破坏的。 INSTANCE; private String name; public String getName() { return name; } }
3、完整代码
/**
* @author Hao
* 单例模式:懒汉式
* 懒汉式是线程不安全的,懒汉式在多线程环境下,可能会出现多个实例,所以需要加锁。
* 懒汉式可以延迟加载,不会在首次加载时就初始化,而是在调用时才创建。
*
* 总结:
* 优点:延迟加载,减少内存开销。
* 缺点:在多线程环境下,可能会出现多个实例。
*/
public class SingletonLazy {
// 私有化构造函数
private SingletonLazy(){}
// 方法一:双重检查锁
// -------------------------方法一开始-------------------------
// 私有化定义实例
private static volatile SingletonLazy instance;
/**
* 提供公共获取实例方法
* @return SingletonLazy 实例
* 使用双重判断加锁,而不直接于方法上加锁,是为了避免每次调用getInstance()方法时,都进行加锁,造成不必要的开销。
*/
public static SingletonLazy getInstance(){
// 第一次判断,判断是否已经实例化,如果已经实例化,那么就不会被多线程影响出现创建多个实例的情况,所以直接返回
if (instance == null){
// 如果没有实例化,那么就加锁,避免多线程下,多个线程同时创建实例的情况
synchronized (SingletonLazy.class){
// 此时进行第二次判断,避免多个线程同时访问抢锁,导致创建多个实例。举个例子
// 线程a、b同时访问,此时instance还未实例化。那么此时线程a、b都会通过第一次判断。
// 这时线程a率先强到锁,如果不进行二次判断,线程a创建实例后,线程b获得锁也会创建实例,从而造成多个实例。
if (instance == null){
instance = new SingletonLazy();
}
}
}
return instance;
}
// -------------------------方法一结束-------------------------
// -----------------------------------------------------------------------------------------------------------------
// 方法二:静态内部类
// -------------------------方法二开始-------------------------
// 创建私有内部类,提供实例
private static class SingletonHolder{
private static final SingletonLazy instance = new SingletonLazy();
}
/**
* 提供公共获取实例方法
* @return SingletonLazy 实例
*
* 使用静态内部类,避免了线程不安全,延迟加载,效率更高。
* 静态内部类在第一次使用的时候才会被加载,不会造成内存浪费。
*/
public static SingletonLazy getSingleton(){
return SingletonHolder.instance;
}
// -------------------------方法二结束-------------------------
}