简述八种单例模式


1.饿汉模式(可以使用)

先来第一种常见饿汉模式:

//饿汉式
class Dog{

    private Dog() {
    }

    private final static Dog instance = new Dog();

    public static Dog createDog(){
        return instance;
    }
}

构造器私有化是为了不让你像平时一样 Dog dog = new Dog(); 这样new一个对象。
private final static Dog instance = new Dog();这种方式在类加载时就完成了初始化。
我们要用的话只需要 类名.静态方法名即可。

public class DanLi {
    public static void main(String[] args) {
        Dog dog = Dog.createDog();
        Dog dog2 = Dog.createDog();
        System.out.println(dog == dog2);		//结果 true
        System.out.println(dog.hashCode());		//结果 356573597
        System.out.println(dog2.hashCode());	//结果 356573597
        //这里可以看两次的hashCode也是相同的,说明用的都是同一个
    }
}

2.饿汉式 (静态代码块,可以使用)

饿汉式还有一种就是放在静态代码块里的写法,其实跟第一种差不多。

//饿汉式 (静态代码块)
class Cat{
    private static Cat cat;

    static {
        cat = new Cat();
    }

    private Cat() {}
    public static Cat createDog(){
        return cat;
    }
}

以上这两种方法都是饿汉模式,它是在类加载时就完成了初始化,所以类加载较慢,也就是说不管你用没用到它,它都已经创造了一个对象等着你用,即使你全程都没有用到它,这就是饿汉式,是不是感觉跟它的名字很合得来。它也有一个小缺点,会造成内存的浪费。

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

class Cat2{
    private static Cat2 cat2;

    private Cat2() {
    }

    public static Cat2 createDog(){
        if (cat2 == null){
            cat2 = new Cat2();
        }
        return cat2;
    }
}

那懒汉式就是反着来了,它一开始没有给你创建好对象,等你需要用,调用它的时候才给你创建。就像咱们平时完成老师布置的作业,有些人当天就完成了老师布置的作业,等到要交的时候也能立马拿出来;有些人要等到交作业截止时间到了才想起有作业,不过最终结果都是大家都交了作业,最终都能给你创建好一个对象。它是在静态方法里加了一层判断,判断它是不是为空,在单线程下第一次创建一个cat2对象 if 判断的结果为true,new了一个对象;当第二次调用在进来的时候,if判断结果为false所以就不会再去new一个新的对象,这就满足了单例模式的基本需求。

再仔细想想它其实是有局限性的,在多线程下的话会出现线程安全问题,可以来分析一下: 第一个线程进来createDog方法,if判断结果为true,没问题,进入if,但是它还没来得及往下执行,也就是没有创建对象,此时第二个线程进来了由于第一个线程没有执行new Cat2();第二个线程进入if的判断结果为true,第二个线程也进来了并且执行了new语句一路往下走,第一个线程的下一步也是new语句,等第一个也走完这样就有了两个对象了,虽然达到懒加载的效果但是就不是单例模式了。开发过程中也不会用这种方式。

4.懒汉式 (解决了线程不安全问题,效率低)

上一方法不是存在安全问题嘛,欧克好办,那加一个锁总可以了吧,就在方法上加一个synchronized 关键字,其他不动。

class Cat3{
    private static Cat3 cat3;

    private Cat3() {
    }

    public synchronized static Cat3 createDog(){
        if (cat3 == null){
            cat3 = new Cat3();
        }
        return cat3;
    }
}

这样确实解决了线程安全问题,在开发过程中createDog方法肯定是会经常用的,在整个方法上进行同步会导致效率太低了。

5.懒汉式 (不太靠谱,线程不安全)

上一个方法在方法上加synchronized不是效率太低嘛,欧克好办,搞一个同步代码块总可以了吧。

class Cat4{
    private static Cat4 cat4;

    private Cat4() {
    }

    public static Cat4 createDog(){
        if (cat4 == null){
            synchronized (Cat4.class){
                cat4 = new Cat4();
            }
        }
        return cat4;
    }
}

其实仔细分析一下这种方法更太靠谱,它连线程安全都没有保证,可以套用第三种懒汉式的分析逻辑来分析,不难发现此方法存在线程安全问题。

6.懒汉式 (线程安全,双重检查,推荐使用)

上一方法不是存在安全问题嘛,欧克好办,再加一层判断,双重检查,这总行了吧。

//volatile 解决双重检查指令重排序问题
class Cat5{
    private static volatile Cat5 cat5;

    private Cat5() {
    }

    public static Cat5 createCat(){
        if (cat5 == null){
            synchronized (Cat5.class){
                if (cat5 == null){
                    cat5 = new Cat5();
                }
            }
        }
        return cat5;
    }
}

当两个线程都在第一个if里面的时候,由于同步代码块里面只能进一个线程,所以不管其中哪个线程创建好了对象,另一个线程会再次判断cat5是否为空。再下一次调用createCat也不用担心,它会在第一层判断就挡住了,结果为false直接return ,这样就不用再次进入同步代码块里面去,是不是比上一个高效。

7.懒汉式 (线程安全,静态内部类,推荐使用)

还有一种就是运用了静态内部类的方法。

class Cat6{

    private Cat6() {
    }
    private static final class CatInstance{
        private static final Cat6 CAT6= new Cat6();
    }

    public static Cat6 createDog(){
        return CatInstance.CAT6;
    }
}

它是利用了类加载的机制保证了初始化实例时只有一个线程,JVM帮助我们保证了线程的安全性,类Cat6装载的时候,此静态内部类并不会马上被装载,等碰到使用了才会被装载,在装载类的时候是线程安全的。

8.枚举(线程安全,推荐使用)

还有一种用枚举的方法。

//枚举
enum Cat7{
    Instance;
    public static void say(){
        System.out.println("Hello");
    }
}


public class DanLi {
    public static void main(String[] args) {
        Cat7 instance = Cat7.Instance;
        Cat7 instance2 = Cat7.Instance;
        System.out.println(instance == instance2);	//true
        Cat7.sayOk();								//输出:Hello
    }
}

总结

单例模式主要还是分为饿汉式和懒汉式两种,每种都有不同的写法和各自的优缺点。懒汉式有好多种,上述也算讲了一步一步的改进演化过程,真正开发能用的也没几种,在开发的过程中还需要自己衡量使用哪种才能达到最好的效果。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值