定义:
确保一个类只有一个实例,并提供一个全局访问点。
简介:
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,阻止所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
首先,我们来看一下经典单例模式的实现。
如果现在有两个线程都要执行getInstance()方法,可能会出现如下情况。
从图中可以看到,这里生成了两个对象,违反了单个实例的原则。
利用这个做法,我们依赖JVM在加载这个类时马上创建此类唯一的实例。JVM保证在任何线程访问instance静态变量之前,一定会先创建此实例。
如果应用程序总是创建并使用单例,或者在创建和运行方面的负担不太繁重,可以使用该方式。
最后总结一下单例模式的要点:
1.单例模式确保程序中一个类最多只有一个实例。
2.单例模式也提供访问这个实例的全局点。
3.在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。
4.确定在性能和资源上的限制,然后小心的选择适当的方案来实现单例,以解决多线程的问题。
5.如果使用多个类加载器,可能导致单例失效而产生多个实例。
确保一个类只有一个实例,并提供一个全局访问点。
简介:
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,阻止所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
首先,我们来看一下经典单例模式的实现。
package net.csdn.blog.ruancoder;
public class Singleton {
// 利用一个静态变量来记录Singleton类的唯一实例
private static Singleton instance;
// 这里可以添加其他有用的变量
// 把构造器声明为私有的,只有在Singleton类内才可以调用构造器
private Singleton() {
}
// 利用getInstance()方法实例化对象,并返回这个实例
public static Singleton getInstance() {
// 如果instance是空的,表示还没有创建实例
if (instance == null) {
// 利用私有的构造器产生一个Singleton实例并赋值给instance静态变量
// 如果我们不进入该方法,它就永远不会产生
instance = new Singleton();
}
// 当执行到这个return,就表示我们已经有了实例,并将instance返回
return instance;
}
// 这里可以添加其他有用的方法
}
如果现在有两个线程都要执行getInstance()方法,可能会出现如下情况。
从图中可以看到,这里生成了两个对象,违反了单个实例的原则。
那么如何解决呢?只需要把getInstance()方法变成同步(syncronized)方法,多线程灾难几乎就可以轻易地解决了。
同步getInstance()方法
package net.csdn.blog.ruancoder;
public class Singleton {
private static Singleton instance;
private Singleton() {
}
// 通过增加synchronized关键字到getInstance()方法中,我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法
// 也就是说,不会有两个线程可以同时进入这个方法
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种实现在解决问题的同时,会带来性能的降低。只有第一次执行此方法,才真正需要同步。换句话说,一旦设置了instance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。
急切实例化
package net.csdn.blog.ruancoder;
public class Singleton {
// 在静态初始化器中创建单利,保证了线程安全
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
// 已经有实例了,直接返回
return instance;
}
}
利用这个做法,我们依赖JVM在加载这个类时马上创建此类唯一的实例。JVM保证在任何线程访问instance静态变量之前,一定会先创建此实例。
如果应用程序总是创建并使用单例,或者在创建和运行方面的负担不太繁重,可以使用该方式。
双重检查加锁
package net.csdn.blog.ruancoder;
public class Singleton {
// volatile关键字,确保当instance变量被初始化成Singleton实例时,多个线程正确地处理instance变量
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
// 检查实例,如果不存在,就进入同步区块
if (instance == null) {
// 注意,只有第一次才彻底执行这里的代码
synchronized (Singleton.class) {
// 进入区块后,再检查一次,如果仍是空,才创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
利用双重检查加锁,首先检查实例是否已经创建了,如果尚未创建,才进行同步。这样一来,只有第一次会同步,这正是我们想要的。
如果性能是我们关心的重点,那么这个做法可以大大地减少getInstance()的时间消耗。最后总结一下单例模式的要点:
1.单例模式确保程序中一个类最多只有一个实例。
2.单例模式也提供访问这个实例的全局点。
3.在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。
4.确定在性能和资源上的限制,然后小心的选择适当的方案来实现单例,以解决多线程的问题。
5.如果使用多个类加载器,可能导致单例失效而产生多个实例。