顾名思义,这是一个饿汉子,上来就将食物塞进去(实例化),这种方式最简单粗暴
实现方式
/** * 饿汉式单例模式 */ 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使用权之后就会从停止的地方继续开始执行未完成的代码,此时就会再一次实例化,出现多次实例化的情况。因此这种情况在多线程情况下并不能保证单例。
为了解决懒汉式多线程情况下多次实例化的问题,简单粗暴的方式就是加锁,直接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
方法了,有没有办法再优化一下呢,下面就是双重检查的实现方式了。
在线程进入时判断一次,为空则加锁,进入之后再判断一次,至于为什么要再判断一次,这是为了防止两次实例化。假设线程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
为什么要加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
借助于内部类来实例化单例,秀不秀?
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
本文由博客一文多发平台 OpenWrite 发布!