(转载)java单例模式的6种写法
程序员文章站
2022-04-10 08:37:24
...
第一种(懒汉,线程不安全)
public class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton getInstance()
{
if(null == instance)
{
instance = new Singleton();
}
return intance;
}
}
这种写法lazy loading很明显,但致命的是在多线程不能正常工作。
第二种(懒汉,线程安全)
public class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static synchronized Singleton getInstance()
{
if(null == instance)
{
instance = new Singleton();
}
return intance;
}
}
这种写法能够在多线程中很好的工作,而且看起来它齐备很好的lazy loading,但是效率很低
第三种(饿汉)
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton getInstance()
{
return intance;
}
}
这种方式基于classloader机制避免了多线程的同步问题,不过instance在类装载时就实例化,没有达到lazy loading的效果。
第四种(静态内部类)
public class Singleton
{
private Singleton()
{
}
public static Singleton getInstance()
{
return SingletonHolder.INSTANCE;
}
priate static class SingletonHolder
{
private static final Singleton INSTANCE = new Singleton();
}
}
这种方式同样利用classloader的机制来保证初始化instance是线程安全的,并且是lazy loading的
第五种(枚举)
public enum Singleton
{
INSTANCE;
public oid whateverMethod()
{
}
}
这种方式是Effectie Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化后重新创建新的对象,可谓是很坚强的壁垒。不过1.5才引入的enum特性,用这种方式不免让人觉得生疏。
第六种(双重校验锁)
public class Singleton
{
private volatile static Singleton singleton;
public static Singleton getSingleton()
{
if(singleton == null)
{
synchronized(Singleton.class)
{
if(singleton == null)
{
singleton = new Singleton();
}
}
}
}
return singleton;
}
在JDK1.5之后,该方法才能正常达到单例效果
总结
有两个问题需要注意:
1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,他们就都会有各自的实例。
2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个该对象,就会有多个单例类的实例
对第一个问题的修复办法是:
private static Class getClass(String classname) throws ClassNotFoundException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if(null == loader)
{
loader = Singleton.class.getClassLoader();
}
return loader.loadClass(classname);
}
对第二个问题的修复办法是:
public class Singleton implements java.io.Serializable
{
public static Singleton INTANCE = new Singleton();
private Singleton()
{
}
private Object readResolve()
{
return INSTANCE;
}
}
public class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton getInstance()
{
if(null == instance)
{
instance = new Singleton();
}
return intance;
}
}
这种写法lazy loading很明显,但致命的是在多线程不能正常工作。
第二种(懒汉,线程安全)
public class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static synchronized Singleton getInstance()
{
if(null == instance)
{
instance = new Singleton();
}
return intance;
}
}
这种写法能够在多线程中很好的工作,而且看起来它齐备很好的lazy loading,但是效率很低
第三种(饿汉)
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton getInstance()
{
return intance;
}
}
这种方式基于classloader机制避免了多线程的同步问题,不过instance在类装载时就实例化,没有达到lazy loading的效果。
第四种(静态内部类)
public class Singleton
{
private Singleton()
{
}
public static Singleton getInstance()
{
return SingletonHolder.INSTANCE;
}
priate static class SingletonHolder
{
private static final Singleton INSTANCE = new Singleton();
}
}
这种方式同样利用classloader的机制来保证初始化instance是线程安全的,并且是lazy loading的
第五种(枚举)
public enum Singleton
{
INSTANCE;
public oid whateverMethod()
{
}
}
这种方式是Effectie Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化后重新创建新的对象,可谓是很坚强的壁垒。不过1.5才引入的enum特性,用这种方式不免让人觉得生疏。
第六种(双重校验锁)
public class Singleton
{
private volatile static Singleton singleton;
public static Singleton getSingleton()
{
if(singleton == null)
{
synchronized(Singleton.class)
{
if(singleton == null)
{
singleton = new Singleton();
}
}
}
}
return singleton;
}
在JDK1.5之后,该方法才能正常达到单例效果
总结
有两个问题需要注意:
1、如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,他们就都会有各自的实例。
2、如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个该对象,就会有多个单例类的实例
对第一个问题的修复办法是:
private static Class getClass(String classname) throws ClassNotFoundException
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if(null == loader)
{
loader = Singleton.class.getClassLoader();
}
return loader.loadClass(classname);
}
对第二个问题的修复办法是:
public class Singleton implements java.io.Serializable
{
public static Singleton INTANCE = new Singleton();
private Singleton()
{
}
private Object readResolve()
{
return INSTANCE;
}
}