为了账号安全,请及时绑定邮箱和手机立即绑定

设计模式---单例设计模式

标签:
Java

单例设计模式概念

  • 什么是单例设计模式

单例设计模式主要为了解决一个类在内存中只存在一个对象,保证对象的唯一,并设置全局的访问点。

  • 单例模式的特点

1)为了保证对象的唯一,就不能允许其他类中随意创建对象,因此需要将构造函数私有化。

2)根据上一步的说法,在其他类中不能创建对象,那就需要在本类中创建对象。

3)其他类中不能创建该类的对象,也就不能调用成员方法和成员属性,该类中需要提供公共静态的获取对象的方法。

  • 有哪些常见对象是单例的

比如:

1)ServletContext

2)ServletConfig

3)ApplicationContext

  • 常见的单例设计模式

1)饿汉式单例设计模式

2)懒汉式单例设计模式

3)注册式单例设计模式

4)ThreadLocal单例设计模式

一、饿汉式单例设计模式

  • 重点:在单例类中首次加载时就创建实例

  • 单例类

public class Singleton {
    //私有构造方法
    private Singleton(){}

    //实例化对象,需要静态,全局唯一的实例,可以将其声明为final
    private static final Singleton singleton = new Singleton();

    //提供外部访问的方法,外部没有对象,需要用类名调用,方法静态
    public static Singleton newInstance(){
        return singleton;
    }
}
  • 缺点:因为是类加载的时候就开始实例化对象,因此,就算该对象不会被使用,也会被实例化,导致浪费空间。

二、懒汉式单例设计模式

  • 重点:不同于饿汉式,懒汉式单例设计模式只有被调用时才会创建实例。

  • 单例类

public class Singleton {
    //私有构造方法
    private Singleton() {}

    //声明对象,volatile是解决重排序的问题
    private volatile static Singleton singleton = null;

    //提供公共的访问方法
    public static Singleton newInstance(){
        //因为synchronized相对低效,可以考虑用这个判断减少锁判断
        if(singleton == null){
            /*如果是多线程环境里面,可能会出现线程安全的问题
                比如:A线程进来,还为修改状态,singleton仍然是null
                这个时候B线程也能进来。
                所以考虑用synchronized锁住
             */
            synchronized (Singleton.class) {
                //为了保证只有一个实例,不能每次都创建,用null判断
                if(singleton == null) {
                    return singleton;
                }
            }
        }
        return singleton;
    }
}

三、使用静态内部类编写懒汉式

  • 单例类
public class InnerSingleton {
    //私有化构造方法
    private InnerSingleton() {}

    //提供公共获取对象的方法
    public static final InnerSingleton newInstance(){
        //调用静态内部类中的静态属性
        return LazyHolder.innerSingleton;
    }
    //定义一个内部类,在内部类中创建静态的InnerSingleton对象
    private static class LazyHolder{
        private static final InnerSingleton innerSingleton = new InnerSingleton();
    }
}
  • 重点

1)在这种方式作用没有用到synchronized锁

2)利用内部类特征,LazyHolder里面的逻辑需要等待外部方法调用时才执行,简单了说,只有在newInstance被调用了的时候,才会执行LazyHolder里面创建对象的逻辑。

综合上面:可以将其理解为懒汉式单例模式

四、注册式单例设计

  • 概念:将每一个实例都缓存到统一的容器中,使用唯一标识符获取实例,可以考虑使用枚举或者容器的方式实现。

1)枚举式单例设计

  • 单例类
public enum EnumSingleton {
    INSTANCE;

    public static EnumSingleton newInstance(){
        return INSTANCE;
    }
}
  • 枚举类单例模式的测试
public class Test {
    public static void main(String[] args) {
        EnumSingleton e1 = EnumSingleton.newInstance();
        EnumSingleton e2 = EnumSingleton.newInstance();
        System.out.println(e1 == e2);
    }
}
  • 结论:通过执行上述的测试代码,容易得出e1和e2是相等的

  • 重点

枚举在设计的时候,就确定了枚举不被序列化和反射进行破坏

2)容器式实现单例设计

  • 单例类
public class ContainerSingleton {
    //私有化构造方法
    private ContainerSingleton(){}

    //声明一个map的容器
    private static Map<String,Object> map = new ConcurrentHashMap<>();

    //提供一个公共的方法
    public static Object getBean(String className){
        //为了保证getBean是安全的
        synchronized (map) {
            if (!map.containsKey(className)) {
                Object obj = null;
                try {
                    //使用反射实现创建对象
                    obj = Class.forName(className).newInstance();
                    //将创建的对象添加到容器中
                    map.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //将obj对象返回
                return obj;
            }
        }
        //如果没有进入到if里面,则说明该对象已经存在,直接从map容器中获取即可
        return map.get(className);
    }
}
  • 结论:在Spring中就是采用容器式的单例设计模式

五、ThreadLocal单例设计

  • ThreadLocal在线程内部里面,是线程安全的,在ThreadLocal源码中,定义了ThreadLocalMap容器,在这个Map容器中将一个线程作为Key,将具体的对象作为值,因此ThreadLocal也算是注册式的容器类型单例设计模式。
  • 单例类
public class ThreadLocalSingleton {
    //私有化构造方法
    private ThreadLocalSingleton(){}

    //使用ThreadLocal并重写initialValue方法创建对象
    private static final ThreadLocal<ThreadLocalSingleton> threadLocal =
    new ThreadLocal<ThreadLocalSingleton>(){
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };
    //提供公共的方法
    public static ThreadLocalSingleton newInstance(){
        return threadLocal.get();
    }
}
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消