首先我们先熟悉一个概念:懒加载 Lazy loading 懒加载:其实就是延时加载,即当对象需要用到的时候再去加载。
这种方式基于ClassLoader机制避免了多线程的同步问题。不过 instance在类装载是就实例化,在单例模式中大多数都是调用 getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方法(或者其他的静态方法)导致类装载,这时候初始化instance就没达到 lazy loading(懒加载) 的效果
优点与缺点:
优点:写法简单,就是在类装载的时候就完成实例化。避免了线程同步问题(没有多线程问题,早于线程创建)。 (为什么会是类装载就可以完成实例化?–传送门)
缺点:在类装载的时候就完成实例化,没有达到Lazy loading 的效果。从头到尾没有使用这个实例的话,会对内存造成浪费
结论:这种单例模式可用,但是造成内存的浪费
↓ 代码实现:
/* 饿汉式 (饥渴,上来就干) 技术:静态常量 */ public class StaticFinal { // 单例第一步:构造器私有化,外面不能new private StaticFinal() { System.out.println("实例化"); } // 2:本类内部创建 静态常量 显示赋值 private static final StaticFinal instance = new StaticFinal(); // 3:提供一个静态方法,返回实例对象 public static StaticFinal getInstance() { return instance; } } //测试类 public static void main(String[] args) { StaticFinal.getInstance(); }↓ 代码实现:
public class StaticBlock { private StaticBlock() { System.out.println("实例化"); } private static StaticBlock instance; static { instance = new StaticBlock(); System.out.println("静态代码块"); } public static StaticBlock getInstance() { return instance; } } //测试类 public static void main(String[] args) { StaticFinal.getInstance(); }↓ 代码实现:
public class ThreadUnsafe { // 1、首先接口私有化 private ThreadUnsafe() { } //2、创建私有静态变量 private static ThreadUnsafe instance; // 提供一个静态的公有方法,当使用到该方法时,才去创建 instance // 懒汉式 public static ThreadUnsafe getInstance() { if (instance == null) { instance = new ThreadUnsafe(); } return instance; } } //测试类 public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ ThreadUnsafe.getInstance(); }).start(); } }↓ 代码实现:
public class ThreadUnsafe { // 1、首先接口私有化 private ThreadUnsafe() { } //2、创建私有静态变量 private static ThreadUnsafe instance; // 提供一个静态的公有方法,当使用到该方法时,才去创建 instance // 懒汉式 public static ThreadUnsafe getInstance() { if (instance == null) { synchronized (ThreadUnsafe.getInstance()){ instance = new ThreadUnsafe(); } } return instance; } } // 测试 public static void main(String[] args) { new Thread(() -> { for (int i = 0; i < 10; i++) { StaticSafe.getInstance(); } }).start(); }优点:
Double-Check 概念是多线程开发中常使用到,如代码中所示,我们进行了俩次 if(instance==null) 检查,这样就保证线程安全了这样,实例化代码只用执行一次,后面再次访问时,判断 if(instance==null),直接return 实例化对象,也避免反复进行方法同步线程安全;延迟加载;效率高结论:在实际开发中,推荐使用这种单例设计模式↓ 代码实现:
public class DoubleCheck { private DoubleCheck() { System.out.println(Thread.currentThread().getName()); } private static volatile DoubleCheck INSTANCE; // 提供了一个静态的公有方法,加入双重检测代码,解决线程安全问题,同时解决懒加载问题 // 同时保证了效率 public static DoubleCheck getInstance() { if (instance == null) { synchronized (DoubleCheck.class) { if (instance == null) { instance = new DoubleCheck(); } } } return instance; } // 测试 public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { DoubleCheck.getInstance(); }).start(); } } }