带你认识单例模式之七种不同实现方式,最后一种堪称王炸,你绝对没见过!

单例模式之七种不同实现方式

一、饿汉式

顾名思义,这是一个饿汉子,上来就将食物塞进去(实例化),这种方式最简单粗暴

实现方式

/**
 * 饿汉式单例模式
 */
public final class HungrySingleton {

    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
        System.out.println("HungrySingleton is created");
    }

    public static HungrySingleton getInstance() {
        return instance;
    }

}

测试代码

public static void testHungrySingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName());
                    HungrySingleton instance = HungrySingleton.getInstance();
                    System.out.println(instance);
                }
            }
        }, "Thread" + i).start();
    }
}

运行结果

HungrySingleton is created

二、懒汉式

懒汉式,这个方法非常懒,就像我上学时一样,作业不写,偏偏等到要上学的前几天才开始动手疯狂补作业。

/**
 * 懒汉式单例模式
 * 不要一开始就初始化实例,而是等到要用到的时候再去初始化实例
 */
public final class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {
        System.out.println("LazySingleton constructor");
    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

}

测试代码

/**
* 懒汉式单例模式
*/
public static void testLazySingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                LazySingleton instance = LazySingleton.getInstance();
            }
        }, "Thread" + i).start();
    }
}

运行结果

LazySingleton constructor
LazySingleton constructor
LazySingleton constructor
LazySingleton constructor
LazySingleton constructor

总结

上面代码在多线程情况下出现了多次实例化的情况,假如线程1正在做if (instance == null)判断,此时CPU开始调度,线程2进入if (instance == null)并且创建了一个实例,然后放弃CPU使用权,当线程1获取到CPU使用权之后就会从停止的地方继续开始执行未完成的代码,此时就会再一次实例化,出现多次实例化的情况。因此这种情况在多线程情况下并不能保证单例

三、synchronized方式

为了解决懒汉式多线程情况下多次实例化的问题,简单粗暴的方式就是加锁,直接getInstance方法上锁,保证仅一个线程同时访问此方法。

public class SynchronizedLazySingleton {

    private static SynchronizedLazySingleton instance = null;

    private SynchronizedLazySingleton() {
        System.out.println("Synchronized Lazy Singleton");
    }

    public static synchronized SynchronizedLazySingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedLazySingleton();
        }
        return instance;
    }

}

测试代码

public static void testSynchronizedLazySingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SynchronizedLazySingleton instance = SynchronizedLazySingleton.getInstance();
            }
        }, "Thread" + i).start();
    }
}

运行结果

Synchronized Lazy Singleton

思考

上面加锁的粒度是不是太大了,多线程情况下同时仅有一个线程才能访问getInstance方法了,有没有办法再优化一下呢,下面就是双重检查的实现方式了。

四、双重检查(double check)

在线程进入时判断一次,为空则加锁,进入之后再判断一次,至于为什么要再判断一次,这是为了防止两次实例化。假设线程1进入了第一个判断,此时释放了CPU;线程2进来之后,同样可以执行进入第一个判断并且加锁,然后实例化,最后释放CPU;然后等到线程1再次拿到CPU之后,从上一次未执行的地方开始,对DoubleCheckLazySingleton.class加锁,由于没有第二个判断,此时线程1就可以完成第二次实例化了;因此,这就是为什么要加两次判断的原因。

/**
 * 双重检查懒汉式单例模式
 */
public class DoubleCheckLazySingleton {

    private static DoubleCheckLazySingleton instance = null;

    private DoubleCheckLazySingleton() {
        System.out.println("DoubleCheckLazySingleton is created");
    }

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

}

测试代码

    public static void testDoubleCheckLazySingleton() {
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    DoubleCheckLazySingleton instance = DoubleCheckLazySingleton.getInstance();
                }
            }, "Thread" + i).start();
        }
    }

运行结果

DoubleCheckLazySingleton is created

五、双重检查+可见性(double check + volatile)

为什么要加volatile,因为我代码没有写完,如果此时单例类中还有其他的字段,就容易出现空指针异常,因为这些字段对于其它线程是不可见的,需要加上volatile保证对于其它线程也可见。

/**
 * 双重检查懒汉式单例模式+volatile
 */
public class VolatileDoubleCheckLazySingleton {

    private volatile static VolatileDoubleCheckLazySingleton instance = null;

    private VolatileDoubleCheckLazySingleton() {
        System.out.println("VolatileDoubleCheckLazySingleton construct");
    }

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

}

测试代码

/**
* 双重检查单例模式 + volatile
*/
public static void testVolatileDoubleCheckLazySingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                VolatileDoubleCheckLazySingleton instance = VolatileDoubleCheckLazySingleton.getInstance();
            }
        }, "Thread" + i).start();
    }
}

运行结果

VolatileDoubleCheckLazySingleton construct

六、holder内部类实例化

借助于内部类来实例化单例,秀不秀?

public class HolderSingleton {
    private static HolderSingleton instance;

    private HolderSingleton() {
        System.out.println("HolderSingleton constructor");
    }

    private static class Holder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }

}

测试代码

/**
* Holder
*/
public static void testHolderSingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HolderSingleton instance = HolderSingleton.getInstance();
            }
        }, "Thread" + i).start();
    }
}

运行结果

HolderSingleton constructor

七、枚举类实现方式

王炸!枚举类天然的定义就是单例,这没的说。

public enum EnumSingleton {

    INSTANCE,
    ;

    private EnumSingleton() {
        System.out.println("INSTANCE will be initialized immediately");
    }

    public static void method() {

    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }

}

测试代码

/**
* EnumSingleton
*/
public static void testEnumSingleton() {
    for (int i = 0; i < 1000; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                EnumSingleton instance = EnumSingleton.getInstance();
            }
        }, "Thread" + i).start();
    }
}

运行结果

INSTANCE will be initialized immediately
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JavaGPT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值