【Java】深入理解单例模式

设计模式之单例模式

  • 定义
  • 实现
    • 饿汉式
    • 懒汉式
  • 优缺点

单例模式是最简单的一种模式,但却也有很多需要注意的点,单例模式也时常作为求职者的笔试题,能写出怎样的单例模式从某种程度会影响面试官对你的印象。这里从简单的单例模式开始认知单例模式。

定义

单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法

三个要点:

  • 一个实例
  • 自行创建
  • 对外提供这个实例

实现

单例模式的主要实现方法有两种:饿汉式和懒汉式。

饿汉式

饿汉式:不管三七二十一,先创建了再说。

// 饿汉式 单例模式
public class Singleton {

	//需要类持有对象
	private static Singleton instance = new Singleton();

	//私有化构造方法
	private Singleton() { }

	//对外提供示例方法
	public static Singleton getInstance() {
		return instance;
	}

}

注意两个个要点:

  • 构造函数私有化(为了避免外部调用创建对象)
    -变量的static修饰,外部无法创建实例,需要使用类的静态方法

饿汉式是最简单最粗暴的,不存在线程安全问题(懒汉式会有线程安全问题,待会说明)

懒汉式

懒汉式:懒惰嘛,等到用的时候再告诉我,我再创建好了~


// 懒汉式 单例模式
public class Singleton {

	//需要类持有对象
	private static Singleton instance;

	//私有化构造方法
	private Singleton() { }

	//对外提供示例方法
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

懒汉式不会先创建对象,待程序执行时再创建对象。
怎么样,简单吧~

But,问题来了,上面的懒汉式能保证单例吗?
这里假设两条线程并发访问方法时,两个都读到 instance 为null时,就会创建两个对象了,即非单实例。

如何保证懒汉式在多线程下单实例了:使用同步 synchronized 不就可以了
其他代码不变,修改方法即可

	public static synchronized Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}

其实到这里,基本就可以算结束了。懒汉式使用同步方法就能保证多线程下的单例。但同步方法由于需要持有锁,会阻塞其他线程,程序性能偏低。

如何改进?

使用同步代码块

	public static Singleton getInstance() {
		synchronized (Singleton.class) {
			if (instance == null) {
				instance = new Singleton();
			}
		}
		return instance;
	}

但程序依赖效率会低,所有获取这个实例时都会持有锁而阻塞线程。
我们可以再加上一重校验 (双重校验锁)

	public static Singleton getInstance() {
		if (instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}

这样只有对象初始化时才会同步代码块,而其余时间内是不会判断锁的。

优缺点

优点 缺点
饿汉式 无需锁,效率高 浪费内存
懒汉式 第一次调用才初始化,避免浪费内存 需要加锁,会影响效率

你可能感兴趣的:(【Java】深入理解单例模式)