前言:
如今兄弟们对“单例模式双重锁写法”大体比较注重,我们都想要学习一些“单例模式双重锁写法”的相关文章。那么小编同时在网上收集了一些有关“单例模式双重锁写法””的相关内容,希望兄弟们能喜欢,看官们一起来了解一下吧!面试经历
(后来别人跟我说这种写法nacos里也常见)
记录一次面试经历
2023.06.01
美团海笔的,本来以为笔的情况也不咋好 看到牛客网上一堆ak说没面试机会的
结果也不知怎地到了一面
(略过自我介绍和项目介绍~)
面试官:会写单例吗,写个单例看看
我:
java复制代码// 饿汉式public class SingleObject { private static SingleObject instance = new SingleObject(); //让构造函数为 private private SingleObject(){} public static SingleObject getInstance(){ return instance; }}
面试官:嗯 你这个单例在没有引用的时候就创建了对象?优化一下
我:应该是懒汉模式!
java复制代码public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { // 多了一个判断是否为null的过程 if (instance == null) { instance = new Singleton(); } return instance; } }
面试官:你这个线程不安全啊 再优化一下?
我:那就加个锁吧
java复制代码public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
面试官:这种写法 多线程来了的话会阻塞在一起 能否再优化?
我:。。。 不会了
面试官:回去看看双检锁单例
···
之后问了数据库事务
读未提交(read uncommitted)读已提交(read committed)可重复读(repeatable read)序列化(serializable)以及默认是哪个(repeatable read) 、
数据库的范式了解吗 等等
不出意外:
还是非常可惜的 这次机会 再加油吧
单例模式整理
学习自菜鸟教程
1.饿汉式
懒加载 no 多线程安全 yes 缺点:没有实现懒加载,即还未调用就创建了对象
java复制代码public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }2.懒汉式(线程不安全)
懒加载 yes 多线程安全 no
java复制代码public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }3.懒汉式(线程安全)
懒加载 yes 多线程安全 yes 缺点:和面试官说的那样,多线程访问会阻塞
java复制代码public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }4.双检锁/双重校验锁(DCL,即 double-checked locking)
java复制代码public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
这段代码是一个单例模式的实现,使用了双重检查锁定的方式来保证线程安全和性能。双重检查锁定是指在加锁前后都进行了一次判空的操作,以避免不必要的加锁操作。
而为了保证双重检查锁定的正确性,需要使用volatile关键字来修饰singleton变量,以禁止指令重排序优化。(JUC的知识串起来了!)如果没有volatile关键字修饰,可能会出现一个线程A执行了new Singleton()但是还没来得及赋值给singleton,而此时另一个线程B进入了第一个if判断,判断singleton不为null,于是直接返回了一个未初始化的实例,导致程序出错。
使用volatile关键字可以确保多线程环境下的可见性和有序性,即一个线程修改了singleton变量的值,其他线程能够立即看到最新值,并且编译器不会对其进行指令重排序优化。这样就能够保证双重检查锁定的正确性。
学到了 !!!
后来别人提醒:
(牛逼。。)
5.登记式/静态内部类
java复制代码public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
这段代码是一个单例模式的实现,使用了静态内部类的方式来保证线程安全和性能。静态内部类是指在外部类中定义一个静态的内部类,该内部类可以访问外部类的所有静态成员和方法,但是外部类不能访问内部类的成员和方法。
在这个单例模式的实现中,SingletonHolder就是一个静态内部类,它里面定义了一个静态的、final的、类型为Singleton的变量INSTANCE。由于静态内部类只有在被调用时才会被加载,因此INSTANCE对象也只有在getInstance()方法被调用时才会被初始化,从而实现了懒加载的效果。
由于静态内部类的加载是线程安全的,因此不需要加锁就可以保证线程安全。同时,由于INSTANCE是静态final类型的,因此保证了它只会被实例化一次,并且在多线程环境下也能正确地被发布和共享。
这种方式相对于双重检查锁定来说更加简单和安全,因此在实际开发中也比较常用。
6. 枚举
java复制代码public enum Singleton { INSTANCE; public void whateverMethod() { } }
这段代码是使用枚举类型实现单例模式的一种方式。在Java中,枚举类型是天然的单例,因为枚举类型的每个枚举值都是唯一的,且在类加载时就已经被初始化。
在这个示例代码中,Singleton是一个枚举类型,其中只定义了一个枚举值INSTANCE。由于INSTANCE是一个枚举值,因此它在类加载时就已经被初始化,并且保证全局唯一。在使用时,可以通过Singleton.INSTANCE来获取该单例对象。
需要注意的是,虽然枚举类型天然的单例特性可以保证线程安全和反序列化安全,但是如果需要延迟初始化或者有其他特殊需求,仍然需要使用其他方式来实现单例模式。
7. 容器式单例
Java中可以使用容器来实现单例模式,比如使用Spring框架中的Bean容器。下面是一个使用Spring框架实现单例的示例代码:
定义一个单例类,比如MySingleton:
java复制代码public class MySingleton { private static MySingleton instance; private MySingleton() {} public static MySingleton getInstance() { if (instance == null) { instance = new MySingleton(); } return instance; } public void doSomething() { // do something }}在Spring的配置文件中定义该类的Bean:
xml复制代码<bean id="mySingleton" class="com.example.MySingleton" scope="singleton"/>在Java代码中获取该Bean:
java复制代码ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");MySingleton mySingleton = (MySingleton) context.getBean("mySingleton");mySingleton.doSomething();
在上面的代码中,我们在Spring的配置文件中定义了一个名为mySingleton的Bean,它的类是com.example.MySingleton,作用域为singleton(即单例)。然后在Java代码中,我们通过ApplicationContext获取该Bean,并调用它的doSomething()方法。
使用Spring框架可以方便地管理单例对象,同时也可以很容易地实现依赖注入和控制反转等功能。
原文链接:
标签: #单例模式双重锁写法