在整个程序中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象的方法。
构造器私有化,无法通过new创建对象,在类的内部完成对象的创建,对外提供一个静态方法,返回该对象的实例。
/** * 饿汉式静态常量的实现方式 */ public class HungryStaticConstant { //构造器私有化,无法通过new创建对象, private HungryStaticConstant(){} private final static HungryStaticConstant hungryStaticConstant = new HungryStaticConstant(); //对外提供一个静态方法,返回该对象的实例 public static HungryStaticConstant getInstance(){ return hungryStaticConstant; } }
优点: 写法简单,类装载的时候完成了实例化,避免了线程同步的问题。
缺点: 没有完成懒加载的效果,如果没有用到这个实例会造成资源浪费。
与静态常量类似
/** * 饿汉式(静态代码块实现) */ public class HungryStaticCodeBlock { //构造器私有化,无法通过new创建对象, private HungryStaticCodeBlock(){} private static HungryStaticCodeBlock hungryStaticCodeBlock; //静态代码块,返回实例对象 static{ hungryStaticCodeBlock = new HungryStaticCodeBlock(); } //对外的静态方法,返回对象的实例 public static HungryStaticCodeBlock getInstance(){ return hungryStaticCodeBlock; } }
优点缺点同饿汉式(静态常量)
/** * 线程不安全的懒汉式实现 */ public class LazyNonSafeThread { private static LazyNonSafeThread lazyNonSafeThread; //构造函数私有化 private LazyNonSafeThread(){} //提供一个静态方法,当使用该方法时才创建实例 public static LazyNonSafeThread lazyNonSafeThread(){ if(lazyNonSafeThread == null){ //没创建时,创建实例 lazyNonSafeThread = new LazyNonSafeThread(); } return lazyNonSafeThread; } }
优点: 起到了懒加载的效果,但是只能在单线程下使用。
缺点: 如果多线程的情况下,多个线程都进入到了if判断的情况中就会产生多个实例,就不是单例模式了。
/** * 懒汉式线程安全的实现方式 */ public class LazySafeThread { private static LazySafeThread lazySafeThread; //构造函数私有化 private LazySafeThread(){} //提供一个静态方法,当使用该方法时才创建实例 public static synchronized LazySafeThread lazySafeThread(){ if(lazySafeThread == null){ //没创建时,创建实例 lazySafeThread = new LazySafeThread(); } return lazySafeThread; } }
问题: 效率太低,获取线程实例的时候每次都要进行同步。
/** * 单例模式双重检查实现方式 */ public class DoubleCheck { //增加volatile关键字 private static volatile DoubleCheck doubleCheck; private DoubleCheck(){} public static DoubleCheck doubleCheck(){ //第一层检查 if(doubleCheck == null){ synchronized (DoubleCheck.class){ //第二层检查 if(doubleCheck == null){ doubleCheck = new DoubleCheck(); } } } return doubleCheck; } }
解决了线程安全问题,同时也解决了懒加载问题。
实际开发中推荐使用这种单例设计模式。
/** * 静态内部类实现单例模式 */ public class StaticInnerClass { private StaticInnerClass(){} //类装载的时候静态内部类不会装载 private static class InnerClass{ private final static StaticInnerClass staticInnerClass = new StaticInnerClass(); } public static StaticInnerClass getInstance(){ return InnerClass.staticInnerClass; } }
当调用getInstance时才会装载内部类,装载时是线程安全的,所以此方法也是线程安全的并且兼顾懒加载的特性。
此外还可以同步借助枚举来实现单例模式,不仅能避免多线程的同步问题,而且还能通过反序列化重新创建对象,也是《EFFECTIVE JAVA》的作者Josh Bloch提倡的方式
在Java中Runtime就是经典的单例模式:
使用场景: 需要频繁创建和销毁对象,重量级对象(创建对象耗时过多或消耗资源过多),经常用到的对象、工具类对象等。