单例模式:顾名思义,当前类的对象在系统中只有一个,因此节省了资源,也免去频繁创建对象的过程。
1、创建对象成本较高,且需要频繁使用的对象。比如系统中的字典工具类,频繁创建不仅有网络传输成本还有IO成本,频繁创建和销毁对于系统而言开销是比较大的。
2、频繁访问数据库或者文件的对象。
3、系统中的全局计数器
4、多线程应用的线程池工具类
5、Web应用中读取配置的工具类对象
这是我们工作中最常用的一种实现方式,第一编码简单,第二线程安全(由JVM来保证),有以下三个特点:a-私有静态的全局常量 b-私有的构造方法 c-公有的全局访问点
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } public static void main(String[] args) { for (int i = 0; i < 100000; i++) { new Thread(() -> System.out.println(Singleton.getInstance())).start(); } } }这种实现方式编码还是比较复杂的,因为需要主要的细节比较多,这里列举一下:a-私有的静态变量需要用volatile修饰,原因(https://blog.csdn.net/jinyouxinzao/article/details/108245039),否则多线程的场景下会出现半初始化状态的对象; b-需要双重检查,第一重的检查是为了避免锁竞争,第二重检查是为了重复创建对象。
public class LazySingleton { // 注意一定要加上volatile,否则万一遇上了指令重排,会读取到半初始化状态的对象 private static volatile LazySingleton INSTANCE = null; private LazySingleton() {} public static LazySingleton getInstance() { // 这一步的判断是为了避免锁竞争 if (INSTANCE == null) { synchronized(LazySingleton.class) { if (INSTANCE == null) { INSTANCE = new LazySingleton(); } } } return INSTANCE; } public static void main(String[] args) { for (int i = 0; i < 100000; i++) { new Thread(() -> System.out.println(LazySingleton.getInstance())).start(); } } }这种方式出自神作《effective java》,是一种高逼格的实现方式,让我真正的见识到了大神是怎样写代码的,这种方式有前面几种方式都无法比拟的几个特点:
4.1 代码空前的简洁
4.2 可以防止个别杠精通过反射来创建对象
4.3 可以防止个别杠精通过反序列化的方式操作对象
以下是枚举单例的代码:
public enum Singleton { INSTANCE; public static void main(String[] args) { for (int i = 0; i < 10000; i++) { new Thread(()->System.out.println(Singleton.INSTANCE.hashCode())).start(); } } }