1):饿汉式(线程安全,调用效率高,但是不能延时加载)
/**
* 饿汉式单例模式
*
* @author Cc
*
*/
public class Singleton {
private static Singleton instance = new Singleton(); // 类初始化时,立即加载这个对象(无延时加载的优势).天然是线程安全的。
// 私有构造器
private Singleton() {
}
// 方法没有同步,调用效率高
private static Singleton getInstence() {
return instance;
}
}
另一种写法:
public class Singleton_1_1 {
private static Singleton_1_1 instance = null;
static {
instance = new Singleton_1_1();
}
private Singleton_1_1() {
}
public static Singleton_1_1 getInstance() {
return instance;
}
}
和上面的没啥区别,都是在类初始化时实例化类
2):懒汉式( 线程安全,调用效率高,可以延时加载)
/**
* 懒汉式
*
* 要点:真正用的时候才加载。
* 缺点:虽然资源利用效率高了,但每次调用getInstence()方法都要同步,并发效率低。
*
* @author Cc
*
*/
public class Singleton_1 {
private static Singleton_1 instance;
// 1.私有构造器
private Singleton_1() {
}
//方法同步,调用效率低
private static synchronizedSingleton_1 getInstence() { //synchronized 加上这个就线程安全,但是效率很低,不加的话,多线程不能正常工作
if (instance == null) {
instance = new Singleton_1();
}
return instance;
}
}
3):双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题)
/**
* 双从检测锁
* @author Cc
*
*/
public class Singleton_2 {
private volatile static Singleton_2 singleton;
private Singleton_2() {
}
public static Singleton_2 getSingleton() {
if (singleton == null) {
synchronized (Singleton_2.class) {
if (singleton == null) {
singleton = new Singleton_2();
}
}
}
return singleton;
}
}
注:在JDK1.5之后,双重检查锁定才能够正常达到单例效果。
4):静态内部类式(线程安全,调用效率高,可延时加载)
/**
* 静态内部类
* 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,
* 它跟恶汉方式不同的是(很细微的差别):恶汉方式是只要Singleton类被装载了,
* 那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,
* instance不一定被初始化。因为SingletonInstence类没有被主动使用,
* 只有显示通过调用getInstance方法时,才会显示装载SingletonInstence类,从而实例化instance。想象一下,
* 如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,
* 因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然
* 是不合适的。
* @author Cc
*
*/
public class Singleton_3 {
private static class SingletonInstence {
private static final Singleton_3 instence = new Singleton_3();
}
public static Singleton_3 getInstence() {
return SingletonInstence.instence;
}
private Singleton_3() {
}
}
5):枚举单例(线程安全,调用效率高,不能延时加载)
public enum Singleton_4 {
/**
* 优点:实现简单,枚举本身就是单例模式。由JVM从根本上提供保障。避免反射和反序列化的漏洞。
* 缺点:无延迟加载,无懒加载效果。
*/
INSTENCE;
/**
* 通过Singleton_4.INSTENCE就可以调用
*/
// 添加自己需要的操作
public void SingletonOperation() {
}
}
.
总结
有两个问题需要注意:
1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
对第一个问题修复的办法是:
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
对第二个问题修复的办法是:
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
}
private Object readResolve() {
return INSTANCE;
}
}
整理自:
尚学堂 (http://www.bjsxt.com/)视频
http://cantellow.iteye.com/blog/838473