`
guojch
  • 浏览: 70269 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Singleton design pattern in java

阅读更多
一篇很好的介绍Java单例模式实现方式的文章。
转自:http://howtodoinjava.com/2012/10/22/singleton-design-pattern-in-java/


Singleton pattern is a design solution where an application wants to have one and only one instance of any class, in all possible scenarios without any exceptional condition. It has been debated long enough in java community regarding possible approaches to make any class singleton. Still, you will find people not satisfied with any solution you give. They can not be overruled either. In this post, we will discuss some good approaches and will work towards our best possible effort.

Sections in this post:

  • Eager initialization
  • Lazy initialization
  • Static block initialization
  • Bill pugh solution
  • Using Enum
  • Adding readResolve()
  • Adding serial version id
  • Conclusion


Singleton term is derived from its mathematical counterpart. It wants us, as said above, to have only one instance. Lets see the possible solutions:

Eager initialization

This is a design pattern where an instance of a class is created much before it is actually required. Mostly it is done on system start up. In singleton pattern, it refers to create the singleton instance irrespective of whether any other class actually asked for its instance or not.

public class EagerSingleton {
	private static volatile EagerSingleton instance = new EagerSingleton();

	// private constructor
	private EagerSingleton() {
	}

	public synchronized static EagerSingleton getInstance() {
		if (instance == null) {
			instance = new EagerSingleton();
		}
		return instance;
	}
}


Above method works fine, but has one performance drawback. The getInstance() method is synchronized and each call will require extra locking/unlocking steps which are necessary only for first time, and never there after.

Lets solve above problem in next method.

Lazy initialization

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. In singleton pattern, it restricts the creation of instance until requested first time. Lets see in code:

public final class LazySingleton {
	private static volatile LazySingleton instance = null;

	// private constructor
	private LazySingleton() {
	}

	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				instance = new LazySingleton();
			}
		}
		return instance;
	}
}


On first invocation, above method will check if instance is already created using instance variable. If there is no instance i.e. instance is null, it will create an instance and will return its reference. If instance is already created, it will simply return the reference of instance.

But, this method also has its own drawbacks. Lets see how. Suppose there are two threads T1 and T2. Both comes to create instance and execute “instance==null”, now both threads have identified instance variable to null thus assume they must create an instance. They sequentially goes to synchronized block and create the instances. At the end, we have two instances in our application.

This error can be solved using double-checked locking. This principle tells us to recheck the instance variable again in synchronized block in given below way:

public class EagerSingleton {
	private static volatile EagerSingleton instance = null;

	// private constructor
	private EagerSingleton() {
	}

	public static EagerSingleton getInstance() {
		if (instance == null) {
			synchronized (EagerSingleton.class) {
				// Double check
				if (instance == null) {
					instance = new EagerSingleton();
				}
			}
		}
		return instance;
	}
}


Above code is the correct implementation of singleton pattern.

Please ensure to use “volatile” keyword with instance variable otherwise you can run into out of order write error scenario, where reference of instance is returned before actually the object is constructed i.e. JVM has only allocated the memory and constructor code is still not executed. In this case, your other thread, which refer to uninitialized object may throw null pointer exception and can even crash the whole application.

Static block initialization

If you have little idea about class loading sequence, you can connect to the fact that static blocks are executed during the loading of class and even before the constructor is called. We can use this feature in our singleton pattern also like this:

public class StaticBlockSingleton {
	private static final StaticBlockSingleton INSTANCE;

	static {
		try {
			INSTANCE = new StaticBlockSingleton();
		} catch (Exception e) {
			throw new RuntimeException("Uffff, i was not expecting this!", e);
		}
	}

	public static StaticBlockSingleton getInstance() {
		return INSTANCE;
	}

	private StaticBlockSingleton() {
		// ...
	}
}


Above code has one drawback. Suppose there are 5 static fields in class and application code needs to access only 2 or 3, for which instance creation is not required at all. So, if we use this static initialization. we will have one instance created though we require it or not.

Next section will overcome this problem.

Bill pugh solution

Bill pugh was main force behind java memory model changes. His principle “Initialization-on-demand holder idiom” also uses static block but in different way. It suggest to use static inner class.

public class BillPughSingleton {
	private BillPughSingleton() {
	}

	private static class LazyHolder {
		private static final BillPughSingleton INSTANCE = new BillPughSingleton();
	}

	public static BillPughSingletongetInstance() {
		return LazyHolder.INSTANCE;
	}
}


As you can see, until we need an instance, the LazyHolder class will not be initialized until required and you can still use other static members of BillPughSingleton class. This is the solution, i will recommend to use. I also use it in my all projects.

Using Enum

This type of implementation recommend the use of enum. Enum, as written in java docs, provide implicit support for thread safety and only one instance is guaranteed. This is also a good way to have singleton with minimum effort.

public enum EnumSingleton {
	INSTANCE;
	public void someMethod(String param) {
		// some class member
	}
}


Adding readResolve()

So, till now you must have taken your decision that how you would like to implement your singleton. Now lets see other problems that may arise even in interviews also.
Lets say your application is distributed and it frequently serialize the objects in file system, only to read them later when required. Please note that, de-serialization always creates a new instance. Lets understand using an example:

Our singleton class is:

public class DemoSingleton implements Serializable {
	private volatile static DemoSingleton instance = null;

	public static DemoSingleton getInstance() {
		if (instance == null) {
			instance = new DemoSingleton();
		}
		return instance;
	}

	private int i = 10;

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}
}


Lets serialize this class and de-serialize it after making some changes:

public class SerializationTest {
	static DemoSingleton instanceOne = DemoSingleton.getInstance();

	public static void main(String[] args) {
		try {
			// Serialize to a file
			ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
					"filename.ser"));
			out.writeObject(instanceOne);
			out.close();

			instanceOne.setI(20);

			// Serialize to a file
			ObjectInput in = new ObjectInputStream(new FileInputStream(
					"filename.ser"));
			DemoSingleton instanceTwo = (DemoSingleton) in.readObject();
			in.close();

			System.out.println(instanceOne.getI());
			System.out.println(instanceTwo.getI());

		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

Output:
20
10


Unfortunately, both variables have different value of variable “i”. Clearly, there are two instances of our class. So, again we are in same problem of multiple instances in application.
To solve this issue, we need to include readResolve() method in our DemoSingleton class. This method will be invoked when you will de-serialize the object. Inside this method, you must return the existing instance to ensure single instance application wide.

public class DemoSingleton implements Serializable {
	private volatile static DemoSingleton instance = null;

	public static DemoSingleton getInstance() {
		if (instance == null) {
			instance = new DemoSingleton();
		}
		return instance;
	}

	protected Object readResolve() {
		return instance;
	}

	private int i = 10;

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}
}


Now when you execute the class SerializationTest, it will give you correct output.

20
20


Adding serial version id

So far so good. Till now, we have solved the problem of synchronization and serialization both. Now, we are just one step behind our correct and complete implementation. And missing part is serial version id.

This is required in condition when you class structure can change in between you serialize the instance and go again to de-serialize it. Changed structure of class will cause JVM to give exception while de-serializing process.

java.io.InvalidClassException: singleton.DemoSingleton; local class incompatible: stream classdesc serialVersionUID = 5026910492258526905, local class serialVersionUID = 3597984220566440782
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at singleton.SerializationTest.main(SerializationTest.java:24)


This problem can be solved only by adding a unique serial version id to class. It will prevent the compiler to throw the exception by telling that both classes are same, and will load the available instance variables only.

Conclusion

After having discussed so many possible approaches and other possible error cases, i will recommend you below code template to design your singleton class which shall ensure only one instance of class in whole  application in all above discussed scenarios.

public class DemoSingleton implements Serializable {
	private static final long serialVersionUID = 1L;

	private DemoSingleton() {
		// private constructor
	}

	private static class DemoSingletonHolder {
		public static final DemoSingleton INSTANCE = new DemoSingleton();
	}

	public static DemoSingleton getInstance() {
		return DemoSingletonHolder.INSTANCE;
	}

	protected Object readResolve() {
		return getInstance();
	}
}


I hope, this post has enough information to make you understand the most common approaches for singleton pattern. Let me know of you thoughts please.

Happy Learning !!
分享到:
评论

相关推荐

    Software Architecture Design Pattern In Java

    1. **单例模式(Singleton Pattern)**: - 定义:确保一个类只有一个实例,并提供一个全局访问点。 - 应用场景:适用于配置管理、线程池等需要全局唯一对象的场合。 2. **工厂模式(Factory Pattern)**: - ...

    Design Pattern In Java.pdf

    《Design Pattern In Java》这本书是关于Java设计模式的指南,由James W. Cooper撰写。设计模式是软件开发中的一种最佳实践,它总结了在特定情境下解决常见问题的经验和方法,为面向对象编程提供了可复用的设计解决...

    designPattern-java.pdf

    《Design Patterns Java Companion》一书由James W. Cooper撰写,于1998年出版,是一本针对Java程序员的经典参考书。本书主要介绍了设计模式的基本概念,并深入探讨了几种关键的设计模式。 #### 创建型模式 创建型...

    JAVA design pattern-java设计模式

    在这个名为"JAVA design pattern-java设计模式"的CHM文件中,我们可能找到了关于如何在Java开发中应用设计模式的详细信息。 设计模式通常分为三大类:创建型、结构型和行为型。创建型模式关注对象的创建,如单例...

    《Java Design Patterns》高清完整英文PDF版

    Learn how to implement design patterns in Java: each pattern in Java Design Patterns is a complete implementation and the output is generated using Eclipse, making the code accessible to all....

    design-pattern-java.pdf

    工厂三兄弟之抽象工厂模式(二) 工厂三兄弟之抽象工厂模式(三) 工厂三兄弟之抽象工厂模式(四) 工厂三兄弟之抽象工厂模式(五) 单例模式-Singleton Pattern 确保对象的唯一性——单例模式 (一) 确保对象的...

    JAVA 23种设计模式(全).Design Pattern_Java模式

    本资料包“JAVA 23种设计模式(全).Design Pattern_Java模式”涵盖了所有23种经典的GOF(GoF,Gang of Four)设计模式,旨在帮助开发者深入理解和应用这些模式。 首先,我们来看一下23种设计模式的分类: 1. **创建...

    JAVA software design pattern

    1. **单例模式(Singleton)**:保证一个类只有一个实例,并提供一个全局访问点。防止多个实例的创建,通常用于配置中心、日志服务等场景。 2. **工厂模式(Factory Method)**:定义一个用于创建对象的接口,让...

    Design Patterns in Java pdf 0分

    ### 单例模式(Singleton Pattern) 单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式非常有用,特别是在需要对系统资源进行集中控制的情况下,例如数据库连接或线程池等。 **实现方式**: - 使用...

    Design Patterns in Java

    - **单例模式**(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。 - **工厂方法模式**(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类。 - **抽象工厂...

    《Software Architecture Design Patterns in Java》原版PDF 下载

    《Software Architecture Design Patterns in Java》是一本专注于Java软件架构设计模式的重要著作。这本书深入探讨了在Java编程环境中如何有效地应用设计模式,以构建高质量、可维护和可扩展的软件系统。设计模式是...

    Head First Design Pattern

    书中主要针对Java语言进行了讲解,但设计模式本身是跨语言的,因此对于任何面向对象编程语言的学习者都有很高的参考价值。 设计模式是软件开发中的经验总结,是对在特定上下文中经常出现的问题的解决方案。它们提供...

    designpattern.zip

    本资源"designpattern.zip"包含了对Java中23种经典设计模式的详细讲解和代码实例,对于中高级Java工程师来说,是提升开发技能的必备学习材料。 设计模式通常分为创建型、结构型和行为型三大类。创建型设计模式关注...

    java设计模式详解,java design pattern

    1. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。单例模式的实现有多种方式,如饿汉式初始化、静态块初始化、懒汉式初始化、线程安全的单例、Bill Pugh实现方式和枚举实现方式。例如...

    design pattern tutorial

    教程内容涉及的标签是“python design pattern”,虽然教程是用Java语言编写,但设计模式的概念和应用是通用的,可以应用于任何面向对象的编程语言,包括Python。因此,无论是Java开发人员还是Python开发人员,都...

    Design Pattern

    ### 设计模式(Design Pattern) #### 一、概述 设计模式是软件开发中的一种最佳实践,它被广泛应用于解决常见的软件设计问题。设计模式的概念来源于建筑领域的架构模式,特别是受到建筑师克里斯托弗·亚历山大的...

    design-pattern

    ##### 3.4 单例模式 (Singleton Pattern) 单例模式确保一个类只有一个实例,并提供一个全局访问点。 1. **确保对象的唯一性** - **目的**:确保某一个类只有一个实例存在。 - **优点**:节省资源,减少内存开销...

    liang-faan_JavaDesignPattern.pdf

    3.1 单例模式(Singleton Pattern) 单例模式确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。单例模式在Java中实现通常涉及到将构造方法设置为私有,并在一个静态方法中创建和返回唯一实例。 3.2 ...

Global site tag (gtag.js) - Google Analytics