面试~设计模式---单例模式

简介: 面试~设计模式---单例模式

1、说说常见的设计模式有哪些?

单例模式、工厂模式、代理模式、观察者模式、装饰器模式、适配器模式等


2、什么是单例模式

简单来说,单列模式是为了保证某个对象在程序的生命周期内在内存中只存在一个实例。即一个类只有一个对象。

它提供了全局访问的方法。


3、为什么要用单例模式?

① 节省内存资源

② 节省时间(分配对象的时间)


4、单例模式的实现方式

饿汉、懒汉、双重检验锁、静态内部类、枚举


5、手写一个单例模式(结合你的面试时间,写双重检验锁可以延伸出更多问题的提问)

对并发熟悉,可以 写双重检验锁这样就有更多的面试提问点了

也可以写饿汉式

要求懒加载,也写静态内部类

/* 双重检验锁  */
public class Singleton{
    private Singleton(){}//构造器私有化,防止new,导致多个实例
    private static volatile Singleton singleton;
    public static Singleton getInstance(){//向外暴露一个静态的公共方法  getInstance
        //第一层检查
        if(singleton == null){
            //同步代码块
            synchronized (Singleton.class){
                 //第二层检查
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
--------------------------------------------------------------------------------------------
/**
* 饿汉式
*/
public class Singleton{
    private static final Singleton singleton  = new Singleton();//饿汉式,初始化时就创建好了实例[使用了final常量-->饿汉式(静态常量)]
    private Singleton(){}//构造器私有化,防止new,导致多个实例
    public static Singleton getInstance(){//向外暴露一个静态的公共方法  getInstance
        return singleton;
    }
}
--------------------------------------------------------------------------------------------
/**
* 饿汉式
*/
public class Singleton{
    private Singleton(){}//构造器私有化,防止new,导致多个实例
    /**
    * 静态内部类,在其内部以静态常量的方式实例化对象
    */
    private static class SingletonInstance{
        private static final Singleton singleton = new Singleton();//常量静态属性,实例化对象[初始化]
    }
    public static Singleton getInstance(){//向外暴露一个静态的公共方法  getInstance
        return SingletonInstance.singleton;
    }
}


双重检验锁

☆ volatile 关键字,可深入到Java VM内存相关

☆ synchronized 关键字,可深入到Java锁机制,高并发相关

☆ new 关键字,可深入到Java VM类加载机制相关


★ 你写的这个程序是怎么保证线程安全的?

首先构造器私有化向外暴露一个静态的公共方法 getSingleton,来向系统提供对象实例

getSingleton 方法 首先先判断实例是否存在,存在直接返回;不存在加 synchronized 锁,为了进一步判断实例是否已经存在,需要再次进行非空判断,判断为null,不存在实例,再创建实例。


★ synchronized起到了什么作用?

锁定对象,限制当前对象只能被一个线程访问


★ synchronized里你传Singleton.class这个参数,起到什么作用,换成别的行不行?

对当前类加锁,使得这个代码块一次只能被一个线程访问

可以换成别的,这里Singleton.class可以换成一个常量字符串或者自己定义一个内部静态Object


★ 那传Singleton.class,常量字符串,自己定义一个内部静态Object有区别吗?

没啥区别,因为这是一个静态方法,相当于是类锁。类锁(全局锁)对应的就是可以锁在该类的class可以锁在classloader对象上。

类锁是所有线程共享的锁,所以同一时刻,只能有一个线程使用加了锁的方法或方法体,不管是不是同一个实例。


★ volatile 起到了什么作用?

加volatile,是为了 禁止指令重排

在执行new 创建单例对象[ singleton = new Singleton();],

指令1分配对象内存空间;指令2初始化对象;指令3设置singleton 引用指向分配好的对象内存空间;

由于指令重排的优化, 可能初始化的指令还没有执行,就先执行了设置引用指向分配好对象的内存空间的指令了。


  • 在单线程环境下是没有问题的,但在多线程环境下会出现问题:另外一个线程会看到一个还没有被初始化的对象


★ 为什么要进行两次非空检查?

保证线程安全,假设有两个线程同时到达 synchronized 语句块,那么实例化代码只会由其中先抢到锁的线程执行一次

而后抢到锁的线程会在第二个 if 判断中发现 singleton 不为 null,所以跳过创建实例的语句

再后面的其他线程再来调用 getInstance 方法时,只需判断第一次的 if (singleton == null) ,然后会跳过整个if块,直接return实例化后的对象。


双重检查:

  • 实现:线程安全,延迟加载、效率也更高


★ 那你知道synchronized关键字实现同步的原理吗?

synchronized在Java虚拟机中使用监视器锁来实现。每个对象都有一个监视器锁,当监视器锁被占用时就会处于锁定状态。

  线程执行一条叫monitorenter的指令来获取监视器锁的所有权。如果此监视器锁的进入数为0,则线程进入并将进入数设置为1,成为线程所有者。如果线程已经拥有该锁,因为是可重入锁,可以重新进入,则进入数加1.如果线程的监视器锁被其他线程占用,则阻塞直到此监视器锁的进入数为0时才能进入该锁。

  线程执行一条叫monitorexit的指令来释放所有权。执行monitorexit的必须是线程的所有者。每次执行此指令,线程进入数减1,直到进入数为0。监视器锁被释放。


★ 你刚才提到的可重入锁是什么概念,有不可重入锁吗?

我说的可重入锁是广义的可重入锁,当然jdk1.5引入了concurrent包,里面有Lock接口,它有一个实现叫ReentrantLock。广义的可重入锁也叫递归锁,是指同一线程外层函数获得锁之后,内层还可以再次获得此锁。可重入锁的设计是为了避免死锁。sun的corba里的mutex互斥锁是一种不可重入锁的实现。自旋锁也是一种不可重入锁,本质上是一种忙等锁,CPU一直循环执行"测试并设置"直到可用并取得该锁,在递归的调用该锁时必然会引起死锁。另外,如果锁占用时间较长,自旋锁会过多的占用CPU资源,这时使用基于睡眠原理来实现的锁更加合适。


★ 你刚才提到了concurrent包,它里面有哪些锁的实现?

常用的有ReentrantLock,它是一种独占锁。ReadWriteLock接口也是一个锁接口,和Lock接口是一种关联关系,它返回一个只读的Lock和只写的Lock。读写分离,在没有写锁的情况下,读锁是无阻塞的,提高了执行效率,它是一种共享锁。ReadWriteLock的实现类为ReentrantReadWriteLock。ReentrantLock和ReentrantReadWriteLock实现都依赖于AbstractQueuedSynchronizer这种抽象队列同步器。

目录
相关文章
|
2月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
29 2
|
14天前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
28 4
|
23天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
6天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
19 1
|
14天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
23 0
|
2月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
2月前
|
设计模式 安全 Java
C# 一分钟浅谈:设计模式之单例模式
【10月更文挑战第9天】单例模式是软件开发中最常用的设计模式之一,旨在确保一个类只有一个实例,并提供一个全局访问点。本文介绍了单例模式的基本概念、实现方式(包括饿汉式、懒汉式和使用 `Lazy<T>` 的方法)、常见问题(如多线程和序列化问题)及其解决方案,并通过代码示例详细说明了这些内容。希望本文能帮助你在实际开发中更好地应用单例模式,提高代码质量和可维护性。
37 1
|
2月前
|
设计模式 缓存 数据库连接
探索PHP中的设计模式:单例模式的实现与应用
在PHP开发中,设计模式是提高代码可复用性、可维护性和扩展性的重要工具。本文将深入探讨单例模式(Singleton Pattern)的基本概念、在PHP中的实现方式以及实际应用场景。单例模式确保一个类仅有一个实例,并提供全局访问点。通过具体代码示例和详细解释,我们将展示如何在PHP项目中有效利用单例模式来解决实际问题,提升开发效率和应用性能。
|
2月前
|
设计模式 缓存 Java
面试题:谈谈Spring用到了哪些设计模式?
面试题:谈谈Spring用到了哪些设计模式?