在整个程序中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象的方法。
饿汉式(静态常量)构造器私有化,无法通过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就是经典的单例模式:
使用场景: 需要频繁创建和销毁对象,重量级对象(创建对象耗时过多或消耗资源过多),经常用到的对象、工具类对象等。



