Java单例模式的几种常见实现方式

Java单例模式的几种常见实现方式

懒汉or饿汉?

  • 懒汉:单例模式延迟加载方式,一开始不给你new对象,你要new再给你new
  • 饿汉:上来就给你new好一个对象,你可以直接用(吃),你也没法new

饿汉:不加锁,线程安全,用起来方便,容易产生垃圾对象

public class SingletonTest1 {
    public static void main(String[] args) {
        //由于构造方法是私有的,则外界无法实例化该类
        //Singleton1 singleton1 = new Singleton1();
        new Thread(() -> {
            Singleton1 singleton1 = Singleton1.getInstance();
        }).start();

        Singleton1 singleton2 = Singleton1.getInstance();
    }
}

class Singleton1 {
    //类加载时直接通过声明调用构造方法初始化
    private static volatile Singleton1 singleton1 = new Singleton1();

    //让构造函数为private,这样该类不会被外部调用实例化对象
    private Singleton1(){
        check();
    }

    //通过静态方法提供外界获取该唯一可用对象的窗口
    //直接给你用,你也别想着什么时候创建了
    public static Singleton1 getInstance() {
        return singleton1;
    }

    //第一次创建实例就打印
    private static void check() {
        System.out.println("第一次创建该类的唯一实例才会打印这句话");
    }
}

输出:

第一次创建该类的唯一实例才会打印这句话

单线程下的单例模式(懒汉,线程不安全)

这种方式只能在单线程环境中实现单例模式

public class SingletonTest1 {
    public static void main(String[] args) {
        //由于构造方法是私有的,则外界无法实例化该类
        //Singleton1 singleton1 = new Singleton1();

        //只可以通过实例化的方式
        Singleton1 singleton1 = Singleton1.getInstance();
        Singleton1 singleton2 = Singleton1.getInstance();
        singleton1.test();
    }
}

class Singleton1 {
    //默认初始化为null
    private static Singleton1 singleton1;

    //让构造函数为private,这样该类不会被外部调用实例化对象
    private Singleton1(){}

    //通过静态方法提供外界获取该唯一可用对象的窗口
    //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
    public static Singleton1 getInstance() {
        if (singleton1 == null) {
            check();
            singleton1 = new Singleton1();
        }
        //第二次创建就不会新建一个对象,而是传回之前已经创建的对象实例
        return singleton1;
    }

    //一个实例方法,用于证明实例化成功
    public void test() {
        System.out.println("测试方法,证明确实获得到了唯一实例");
    }

    //第一次创建实例就打印
    private static void check() {
        System.out.println("第一次创建该类的唯一实例才会打印这句话");
    }
}

输出:

第一次创建该类的唯一实例才会打印这句话
//显然第二次获取实例时只是获得到了之前创建的实例
测试方法,证明确实获得到了唯一实例

多线程下的单例模式(一)(懒汉,线程安全)

区别在于将方法上锁(这里也可以采用重入锁),我们来分析一下具体的工作环境,A线程为了检测是否新建的了实例,对get方法上锁,开始检测对象是否为null,并调用构造方法,返回对象...B线程试图中途获得锁被阻塞,然后等A线程释放锁之后,B获得锁去检测是否创建了对象,发现已经创建,就返回对象释放锁...

public class SingletonTest1 {
    public static void main(String[] args) {
        //由于构造方法是私有的,则外界无法实例化该类
        //Singleton1 singleton1 = new Singleton1();
        new Thread(() -> {
            Singleton1 singleton1 = Singleton1.getInstance();
        }).start();

        Singleton1 singleton2 = Singleton1.getInstance();
    }
}

class Singleton1 {
    //默认初始化为null
    private static Singleton1 singleton1;

    //让构造函数为private,这样该类不会被外部调用实例化对象
    private Singleton1(){}

    //通过静态方法提供外界获取该唯一可用对象的窗口
    //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
    public synchronized static Singleton1 getInstance() {
        if (singleton1 == null) {
            check();
            singleton1 = new Singleton1();
        }
        //第二次创建就不会新建一个对象,而是传回之前已经创建的对象实例
        return singleton1;
    }

    //一个实例方法,用于证明实例化成功
    public void test() {
        System.out.println("测试方法,证明确实获得到了唯一实例");
    }

    //第一次创建实例就打印
    private static void check() {
        System.out.println("第一次创建该类的唯一实例才会打印这句话");
    }
}

输出:

第一次创建该类的唯一实例才会打印这句话

多线程下的单例模式(二)(双重检测)

这里要注意:上锁解锁的很消耗时间空间的,而这里无论对象是否存在,都先上锁,再去检测,显然是没有必要的,当线程很多的时候会造成资源大量的浪费~可以先检测对象是否存在,如果对象不存在再试图申请锁去创建资源,则会节约资源并达到线程安全的目的——双重检测

public class SingletonTest1 {
    public static void main(String[] args) {
        //由于构造方法是私有的,则外界无法实例化该类
        //Singleton1 singleton1 = new Singleton1();
        new Thread(() -> {
            Singleton1 singleton1 = Singleton1.getInstance();
        }).start();

        Singleton1 singleton2 = Singleton1.getInstance();
    }
}

class Singleton1 {
    //默认初始化为null,volatile:主内存 工作内存
    private static volatile Singleton1 singleton1;

    //让构造函数为private,这样该类不会被外部调用实例化对象
    private Singleton1(){}

    //通过静态方法提供外界获取该唯一可用对象的窗口
    //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
    public static Singleton1 getInstance() {
        if (singleton1 == null) {
            synchronized (Singleton1.class) {
                //两个贤臣可能都越过了外层的null检测,所以可能进入后已经不是null了
                if (singleton1 == null) {
                    check();
                    singleton1 = new Singleton1();
                }
            }
        }
        return singleton1;
    }

    //一个实例方法,用于证明实例化成功
    public void test() {
        System.out.println("测试方法,证明确实获得到了唯一实例");
    }

    //第一次创建实例就打印
    private static void check() {
        System.out.println("第一次创建该类的唯一实例才会打印这句话");
    }
}
posted on 2021-01-26 18:14  白泽talk  阅读(238)  评论(0编辑  收藏  举报