单例模式浅谈

109 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

一、单例模式是什么

单例模式是什么呢?就是有且仅有这个实例。spring的bean默认就是使用单例模式。单例要求构造方法是静态的,实例必须是static并且必须有仅有一个,提供一个公有的静态的获取实例的方法。

二、创建单例的方式

  1. 懒汉式
public class LazySingle {
    private static LazySingle lazySingle = null;

    private LazySingle() {}

    public static LazySingle getInstance() {
        if (lazySingle == null) {
            return new LazySingle();
        }
        return lazySingle;
    }
}

懒汉式在一开始不初始化,在需要使用的时候才初始化。如果要保证线程安全,可以对getInstance方法加锁。

  1. 饿汉式
public class HungrySingle {
    private static HungrySingle hungrySingle = new HungrySingle();

    private HungrySingle(){

    }

    public static HungrySingle getInstance(){
        return hungrySingle;
    }
}

类加载的时候就创建,不管用不用,先创建。线程安全。

  1. 双重加锁机制
public class DoubleLockSingle {
    public static DoubleLockSingle doubleLockSingle = null;

    private DoubleLockSingle(){}

    public static DoubleLockSingle getInstance(){
        if (doubleLockSingle==null){
            synchronized (DoubleLockSingle.class){
                if (doubleLockSingle==null){
                    doubleLockSingle  = new DoubleLockSingle();
                }
            }
        }
        return doubleLockSingle;
    }
}

第一种懒汉式的方式如果加锁,每次获取对象都要判断,但双重加锁机制下只有还没有创建实例的情况下需要加锁,继提升了效率,又保证了线程安全。

  1. 使用内部类实现
public class InnerSingle {
    public static class SingleHandler{
        private static InnerSingle innerSingle = new InnerSingle();
    }

    private  InnerSingle(){}

    public static InnerSingle getInstance(){
        return SingleHandler.innerSingle;
    }
}

双亲委派机制保证类只加载一次,实例只有在静态内部类加载的时候创建,保证了线程的安全性。加载外部类的时候,不会立即加载静态内部类,等到调用的时候才会加载,实现了延迟加载。

三、单例的使用场景

单例模式一般在什么情况下使用呢,整个程序只需要一个实例,对象经常创建然后并销毁的,多线程间通信。举个栗子,spring中注入的bean,就是单例的,开始的时候注入容器中,调用的时候都是同一个实例。或者计数器,多个线程调用的时候要保证同一个实例,计数才准确。windows的回收站也是单例模式的,只可以打开一个。

四、单例的优缺点

优点是可以全局访问,可以用于共享一些变量,由于只有一个对象可以提升性能,节省内存空间。缺点就是不适用变化频繁的对象,比如返回数据的,可扩展性差。要注意的是如果对象实例化的对象长时间未使用,系统会认为该对象是垃圾然后进行回收,导致对象状态丢失。

如果有问题,敬请大佬指正。