1.基本介绍
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
2.实现方式
通常单例模式在Java语言中,有两种构建方式:
- 懒汉式—线程不安全:最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能。
- 懒汉式—线程安全:加上synchronize之类保证线程安全的基础上的懒汉模式,相对性能很低,大部分时间并不需要同步
- 饿汉方式。指全局的单例实例在类装载时构建。 [2]
- 双检锁式。在懒汉式基础上利用synchronize关键字和volatile关键字确保第一次创建时没有线程间竞争而产生多个实例,仅第一次创建时同步,性能相对较高
- 登记式。作为创建类的全局属性存在,创建类被装载时创建
- 枚举。java中枚举类本身也是一种单例模式
三大要素:
- 私有的构造方法;
- 指向自己实例的私有静态引用;
- 以自己实例为返回值的静态的公有方法。
3.相关实例
①饿汉式单例(线程安全)
阐述:见名知义,“饿汉”说明它很“饥饿”,迫切需要找到吃的,这里也就是我们所说的实例。为什么说它是线程安全的呢?因为我们一开始就创建一个这样的实例,其他线程在访问前这个实例就已经创建完成,在类的生命周期中只创建一次,所以饿汉单例天生就是线程安全的。
// 饿汉式单例示例
class Singletone {
private static Singletone singleton = new Singletone();
// 懒汉式
private Singletone() {
}
// 保证返回一个实例
public static Singletone getSingleton() {
return singleton;
}
}
public class EHanShi {
public static void main(String[] args) {
Singletone singleton1 = Singletone.getSingleton();
Singletone singleton2 = Singletone.getSingleton();
System.out.println(singleton1==singleton2);
}
}
输出结果为: true
②懒汉单例(需要加双重检查锁来保证线程安全)
阐述: 所谓懒汉模式,就是像一个“懒汉”一样,需要用到创建实例了程序再去创建实例,不需要创建实例程序就“懒得”去创建实例。为什么说它不是线程安全的呢?因为在高并发的情况下懒汉单例就可能创建多个,这样不仅违背了单例的原则,也会在造成使用不同实例造成的线程安全问题,所以懒汉单例需要加锁。
// 懒汉单例 线程不安全 需要双重检查锁保证在JVM中只能有一个实例
class Singleton {
// 使用volatile禁止指令重排序
private static volatile Singleton singleton;
// 懒汉式
private Singleton() {
}
// 保证返回一个实例
public static Singleton getSingleton() {
if (singleton == null) { // 第一层锁
synchronized (Singleton.class) {
if (singleton == null) { //第二层锁
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class LanHanShi {
public static void main(String[] args) {
Singleton s1 = Singleton.getSingleton();
Singleton s2 = Singleton.getSingleton();
System.out.println(s1 == s2);
}
}
输出结果为: true
4.总结
懒汉式和饿汉式区别点:
- 饿汉模式是典型的空间换取时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候就不需要再判断了,节省了运行时间。但如果一直没有人调用,这种浪费的空间就不值得,特别是在空间不足的情况下。
- 懒汉模式是这是一种时间换空间的做法,要想线程安全,大家第一想到的便是下面这种方式,就是在getSingleton方法加上synchronized关键字,但是这种方式也有致命的缺点,那就是并发率太低。