面试必问——设计模式之单例设计模式

相信大家面试的时候,经常会被问到,23种设计模式,你熟悉哪写,手写个单例啥的,当然了,这是对于小程序员的面试,大佬请忽略
单例模式呢,顾名思义就是这个对象只会创建一次。因此他的构造方法必须私有。
那么这只创建一次的对象等你用的时候,又怎么用呢?肯定是需要一个方法返回提供同一个对象,也就是唯一的对象。

单例模式

单例模式(Singleton Pattern): 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有三个要点:

  1. 构造方法私有化;
  2. 实例化的变量引用私有化;
  3. 获取实例的方法共有

因此代码我们可以这样写(通过学习观看,总共7种写法):

1、饿汉式

package com.app.singlecase;

/**
 * @ClassName: HungryMan
 * @Description 单例模式饿汉式
 * @Date:2019/9/29 11:05
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class HungryMan {
    //一个对象
    private final  static HungryMan HUNGRY_MAN=new HungryMan();
    //私有构建公开方法
    private HungryMan(){

    }
    //公开方法 获取这个对象
    public static HungryMan getHungryMan(){
        return HUNGRY_MAN;
    }
}

优点: 简单,使用时没有延迟;在类装载时就完成实例化,天生的线程安全

缺点: 没有懒加载,启动较慢;如果从始至终都没使用过这个实例,则会造成内存的浪费。

2、饿汉式的变种写发

package com.app.singlecase;

/**
 * @ClassName: HungryManTwo
 * @Description 单例模式饿汉式的变种
 * @Date:2019/9/29 11:10
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class HungryManTwo {
    private HungryManTwo(){

    }
    private static final HungryManTwo HUNGRY_MAN_TWO;
    static {
        HUNGRY_MAN_TWO=new HungryManTwo();
    }
    private static HungryManTwo getHungryManTwo(){
        return HUNGRY_MAN_TWO;
    }
}

优缺点同上

3、懒汉式(线程不安全)

package com.app.singlecase;

/**
 * @ClassName: Slacker
 * @Description 单例模式懒汉式
 * @Date:2019/9/29 11:13
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class Slacker {
    private Slacker(){

    }
    private static Slacker slacker;
    public static Slacker getSlacker(){
        //slacker==null存在竞态条件,可能会有多个线程同时进入if语句,产生多个实例
        if(slacker==null){
            System.out.println("第一次进入");
            slacker=new Slacker();
        }
        return slacker;
    }
}

优点: 懒加载,启动速度快、如果从始至终都没使用过这个实例,则不会初始化该实力,可节约资源

缺点: 多线程环境下线程不安全。if (singleton == null) 存在竞态条件,可能会有多个线程同时进入 if 语句,导致产生多个实例

4、懒汉式的变种(线程安全)

package com.app.singlecase;

/**
 * @ClassName: SlackerTwo
 * @Description 单例模式懒汉式线程安全
 * @Date:2019/9/29 11:18
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class SlackerTwo {
    private SlackerTwo(){

    }
    private static SlackerTwo slackerTwo;
    public static synchronized SlackerTwo getSlackerTwo(){
        if(slackerTwo==null){
            slackerTwo=new SlackerTwo();
        }
        return slackerTwo;
    }
}

优点: 解决了上述的安全问题
缺点: 使用synchronized关键字对方法进行加锁,使得每次只有一个线程进入该方法,在多线程下,效率降低。

5、双重死锁

package com.app.singlecase;

/**
 * @ClassName: DoubleCheckLock
 * @Description  单例模式之双重检查锁
 * @Date:2019/9/29 11:24
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class DoubleCheckLock {
    private DoubleCheckLock(){

    }

    private static volatile DoubleCheckLock doubleCheckLock;

    public static DoubleCheckLock getDoubleCheckLock(){
        if(doubleCheckLock==null){
            synchronized (DoubleCheckLock.class){
                if(doubleCheckLock==null){
                    doubleCheckLock=new DoubleCheckLock();
                }
            }

        }
        return doubleCheckLock;
    }
}

优点:线程安全;延迟加载;效率较高。

由于 JVM 具有指令重排的特性,在多线程环境下可能出现 singleton 已经赋值但还没初始化的情况,导致一个线程获得还没有初始化的实例。volatile 关键字的作用:

  • 保证了不同线程对这个变量进行操作时的可见性
  • 禁止进行指令重排序

6、静态内部类

package com.app.singlecase;

/**
 * @ClassName: Inside
 * @Description 单例模式之静态内部类
 * @Date:2019/9/29 11:29
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public class Inside {
    private Inside(){

    }
    //静态内部类
    private static class SingletonInstance {
        private static final Inside INSIDE=new Inside();
    }

    public static Inside getInside(){
        return SingletonInstance.INSIDE;
    }
}

优点: 避免了线程不安全,延迟加载,效率高。
静态内部类的方式利用了类装载机制来保证线程安全,只有在第一次调用getInside方法时,才会装载SingletonInstance内部类,完成Singleton的实例化,所以也有懒加载的效果。

7、枚举(最简单且线程安全外加能防止序列号攻击)

package com.app.singlecase;

/**
 * @ClassName: SingletonEnum
 * @Description 单例模式之枚举模式
 * @Date:2019/9/29 11:32
 * @Author: lk
 * @see
 * @since JDK 1.8
 */
public enum SingletonEnum {
    SINGLETON_ENUM;
    public void getEnumTest(){
        System.out.println("枚举测试");
    }
}

优点: 通过JDK1.5中添加的枚举来实现单例模式,写法简单,且不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

作者也是看的另外一片博客学习的,下面是那个博客的链接
作者学习的博客地址

祝大家面试顺利,一起加油。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值