单例设计模式 (八种方式)

单例设计模式

什么是单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象

注意:

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例。

为什么使用单例设计模式

意图:

保证一个类仅有一个实例,并提供一个访问它的全局访问点对象

主要解决:

一个全局使用的类频繁地创建与销毁。

何时使用:

当您想控制实例数目,节省系统资源的时候。

单例设计模式8 种方法

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举

单例设计模式具体实现

1 )饿汉式(静态常量)实例

代码实现

package single;
public class SingletonTest {
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
//懒汉式(静态变量)
class Singleton {
	// 创建一个私有的静态变量 并进行初始化
	private final static Singleton singletonInstance = new Singleton();
	// 构造器私有化 外部不能new
	private Singleton() {

	}
	// 公开一个方法,获得唯一实例
	public static Singleton getInstance() {
		return singletonInstance;
	}
}
/*
输出结果 
    true 
    singleton.hashCode: 366712642 
    singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优点:这种方式写法简单,在类装载的时候就完成实例化,避免了线程同步问题
  2. 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading 懒加载 的效果,如果从始至终未使用过这个类,则会造成内存的浪费。(懒加载的意思就是什么时候用什么时候才加载
  3. 结论:懒汉式单例设计模式可以用,但是会造成内存的浪费

2 )饿汉式(静态代码块)实例

代码实现

package single;
public class SingletonTest {
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
//懒汉式(静态变量)
class Singleton {
	// 创建一个私有的静态变量
	private static Singleton singletonInstance;
	//  在静态代码块中 进行初始化
	static{
		singletonInstance = new Singleton();
	}
	// 构造器私有化 外部不能new
	private Singleton() { }
	public static Singleton getInstance() {
		return singletonInstance;
	}
}
/*
输出结果 
    true
    singleton.hashCode: 366712642
    singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优缺点与上面一样
  2. 结论:懒汉式单例设计模式可以用,但是会造成内存的浪费

3 )懒汉式(线程不安全)实例

代码实现

package single;

public class SingletonTest02 {
	public static void main(String[] args) {
		System.out.println("懒汉式1 线程不安全");
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
//懒汉式(线程不安全)
class Singleton {
	// 创建一个私有的静态变量
	private static Singleton singletonInstance = null;
	// 构造器私有化 外部不能new
	private Singleton() {  }
	// 提供一个公共的静态方法,用到该方法时才实例化对象 即懒加载
	public static Singleton getInstance() {
		if(singletonInstance == null) {
			singletonInstance = new Singleton();
		}
		return singletonInstance;
	}
}
/*
输出结果:
	懒汉式1 线程不安全
    true
    singleton.hashCode: 366712642
    singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优点:实现了Lazy Loading 懒加载 的效果

  2. 缺点:只能在单线程下使用,如果进入了多线程,if(singletonInstance == null)语句还未来得及向下执行,另一个线程也通过了这个判断语句,会导致产生多个实例,因此在严格意义上它并属于单例设计模式。在多线程下不能使用这种方式

  3. 结论:在实际开发中不要使用这种方式。

4 )懒汉式(线程安全 同步方法)实例

代码实现

package single;

public class SingletonTest01 {
	public static void main(String[] args) {
		System.out.println("懒汉式2  线程安全 同步方法");
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
//懒汉式(线程安全 同步方法)
class Singleton {
	// 创建一个私有的静态变量 并进行初始化
	private static Singleton singletonInstance = null;
	// 构造器私有化 外部不能new
	private Singleton() { }
	//利用synchronized修饰getInstance方法,使之变为同步方法 懒加载
	public synchronized static Singleton getInstance() {
		if(singletonInstance == null) {
			singletonInstance = new Singleton();
		}
		return singletonInstance;
	}

}
/*
输出结果:
	懒汉式2  线程安全 同步方法
    true
    singleton.hashCode: 366712642
    singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优点:解决了线程安全的问题
  2. 缺点:效率太低,每个线程想要执行getInstance()方法时,都要进行同步,实际上这个方法只执行一次实例化就够了,之后就直接return 实例就好了,方法进行同步效率太低
  3. 结论:在实际开发中不推荐这种方式。

5 )懒汉式(线程不安全 同步代码块)实例

有的人也许会认为把上面同步方法换成同步代码块就可以了:如下:

package single;
public class SingletonTest {
	public static void main(String[] args) {
		System.out.println("懒汉式3 线程不安全 同步代码块");
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
//懒汉式(线程安全 同步代码块)
class Singleton {
	// 创建一个私有的静态变量
	private static Singleton singletonInstance = null;
	// 构造器私有化 外部不能new
	private Singleton() {  }
	// 提供一个公共的静态方法,用到该方法时才实例化对象 即懒加载
	public static Singleton getInstance() {
		if(singletonInstance == null) {
			// 在这里设置静态代码块  并不能解决同步问题 所以这种方法不可以
			synchronized (Singleton.class) {
				singletonInstance = new Singleton();
			}
		}
		return singletonInstance;
	}
}
/*
输出结果:
	懒汉式3 线程不安全 同步代码块
    true
    singleton.hashCode: 366712642
    singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优缺点与 3) 一样

  2. 结论:在实际开发中不要使用这种方式。

6)双重检查 实例

代码实现

package single;

public class SingletonTest {
	public static void main(String[] args) {
		System.out.println("双重检查  线程安全");
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
} 
// 双重检查
class Singleton {
	/**
	 	1.volatile
	 		volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的         优化而省略。
		2.volatile 的特性
			保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程		 来说是立即可见的,(即实时刷新)。(实现可见性) 禁止进行指令重排序。(实现有序性)
			volatile 只能保证对单次读 / 写的原子性。i++ 这种操作不能保证原子性。
	*/
	// 创建一个私有的静态变量 并用volatile修饰
	private static volatile Singleton singletonInstance = null;
	// 构造器私有化 外部不能new
	private Singleton() {  }
	// 提供一个公共的静态方法,用到该方法时才实例化对象 加入双重检查的代码 解决线程安全 以及懒加载
	public static Singleton getInstance() {
		// 第一次判断  如果实例化直接返回 否则进行实例化
		if(singletonInstance == null) {
			// 设置静态代码块  保证每次只能进入一个线程
			synchronized (Singleton.class) {
				// 第二次判断 保证只能实例化一次
				if(singletonInstance == null) {
					singletonInstance = new Singleton();
				}
			}
		}
		return singletonInstance;
	}
}
/*
输出结果:
	双重检查  线程安全
	true
	singleton.hashCode: 366712642
	singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 优点:双重检查(double-check) 概念时多线程开发中经常用到的,在代码中我们进行了两次if(singletonInstance == null)判断,解决了线程安全问题
  2. 第一个线程调用完getInstance方法后,其他的线程在第一层判断时,就会直接返回实例,避免反复进入同步代码块,解决了效率问题
  3. 结论:在实际开发中推荐使用这种方式。

7)静态内部类 实例

代码实现

package single;
public class SingletonTest {
	public static void main(String[] args) {
		System.out.println("静态内部类  线程安全 懒加载");
		Singleton singleton = Singleton.getInstance();
		Singleton singleton1 = Singleton.getInstance();
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
// 静态内部类
class Singleton {
    // 原因:
    /*
    	在Singleton进行装载的时候 其静态内部类不进行装载,等到getInstance方法返回静态内部类中
    	常量时静态内部类才进行装载,其常量才进行实例化,且装载过程为线程安全,并实现了懒加载。
    */
	//创建静态内部类,利用其特性 实现线程安全,以及懒加载
	private static class SingletonInstance{
		private static final Singleton INSTANCE = new Singleton();
	}
	// 构造器私有化 外部不能new
	private Singleton() {  }
	// 提供一个公共的静态方法 获得实例
	public static Singleton getInstance() {
		return SingletonInstance.INSTANCE;
	}
}
/*
静态内部类  线程安全 懒加载
	true
	singleton.hashCode: 366712642
	singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 在Singleton进行装载的时候 其静态内部类SingletonInstance并不进行装载,等到需要实例化时调用getInstance方法时,静态内部类SingletonInstance才进行装载,完成Singleton类的实例化,类的静态属性,只有在第一次装载类的时候进行初始化,JVM帮我们实现了线程的安全性,并实现了懒加载。
  2. 优点: 避免了线程的不安全,利用静态类实现延迟加载,效率高。
  3. 结论:在实际开发中推荐使用这种方式。

8 )枚举 实例

代码展示:

package single;

public class SingletonTest {
	public static void main(String[] args) {
		System.out.println("JDK1.5 枚举 线程安全");
		Singleton singleton = Singleton.INSTANCE;
		Singleton singleton1 = Singleton.INSTANCE;
		System.out.println(singleton == singleton1);
		System.out.println("singleton.hashCode: " + singleton.hashCode());
		System.out.println("singleto1.hashCode: " + singleton1.hashCode());
	}
}
// 静态内部类,实现单例
enum Singleton {
	INSTANCE;
	public void hello() {
		System.out.println("hello");
	}
}
/*
JDK1.5 枚举 线程安全
	true
	singleton.hashCode: 366712642
	singleto1.hashCode: 366712642
*/

优缺点与总结

  1. 这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
    这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
  2. 结论:推荐使用

在这里插入图片描述
本人公众号

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值