单例设计模式 |介绍
单例模式是最简单的设计模式之一。有时我们只需要我们类的一个实例,例如多个对象共享的单个 DB 连接,因为为每个对象创建单独的 DB 连接可能成本很高。类似地,应用程序中可以有一个配置管理器或错误管理器来处理所有问题,而不是创建多个管理器。
定义:
单例模式是一种设计模式,它将类的实例化限制为一个对象。
让我们看看实现这样一个类的各种设计选项。如果您对静态类变量和访问修饰符有很好的掌握,这应该不是一项艰巨的任务。方法一:经典实现
Java
// Classical Java implementation of singleton
// design pattern
class Singleton
{
private static Singleton obj;
// private constructor to force use of
// getInstance() to create Singleton object
private Singleton() {}
public static Singleton getInstance()
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
Java
// Thread Synchronized Java implementation of
// singleton design pattern
class Singleton
{
private static Singleton obj;
private Singleton() {}
// Only one thread can execute this at a time
public static synchronized Singleton getInstance()
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
Java
// Static initializer based Java implementation of
// singleton design pattern
class Singleton
{
private static Singleton obj = new Singleton();
private Singleton() {}
public static Singleton getInstance()
{
return obj;
}
}
Java
// Double Checked Locking based Java implementation of
// singleton design pattern
class Singleton
{
private static volatile Singleton obj = null;
private Singleton() {}
public static Singleton getInstance()
{
if (obj == null)
{
// To make thread safe
synchronized (Singleton.class)
{
// check again as multiple threads
// can reach above step
if (obj==null)
obj = new Singleton();
}
}
return obj;
}
}
这里我们声明了 getInstance() static 以便我们可以在不实例化类的情况下调用它。第一次调用 getInstance() 时,它会创建一个新的单例对象,之后它只返回相同的对象。请注意,直到我们需要它并调用 getInstance() 方法时,才会创建 Singleton obj。这称为惰性实例化。
上述方法的主要问题是它不是线程安全的。考虑以下执行顺序。
这个执行序列为单例创建了两个对象。因此这个经典的实现不是线程安全的。方法二:使 getInstance() 同步
Java
// Thread Synchronized Java implementation of
// singleton design pattern
class Singleton
{
private static Singleton obj;
private Singleton() {}
// Only one thread can execute this at a time
public static synchronized Singleton getInstance()
{
if (obj==null)
obj = new Singleton();
return obj;
}
}
这里使用synchronized确保一次只有一个线程可以执行getInstance()。
这种方法的主要缺点是每次创建单例对象时都使用同步是昂贵的,并且可能会降低程序的性能。但是,如果 getInstance() 的性能对您的应用程序并不重要,则此方法提供了一个干净且简单的解决方案。方法 3:Eager 实例化
Java
// Static initializer based Java implementation of
// singleton design pattern
class Singleton
{
private static Singleton obj = new Singleton();
private Singleton() {}
public static Singleton getInstance()
{
return obj;
}
}
这里我们在静态初始化器中创建了单例实例。 JVM 在类加载时执行静态初始化程序,因此保证线程安全。仅当您的单例类很轻并且在整个程序执行过程中使用时才使用此方法。方法 4(最佳):使用“双重检查锁定”
如果您仔细注意到,一旦创建了对象,同步就不再有用了,因为现在 obj 不会为 null 并且任何操作序列都将导致一致的结果。
因此,当 obj 为 null 时,我们只会在 getInstance() 上获取一次锁。这样我们只同步第一种方式,正是我们想要的。
Java
// Double Checked Locking based Java implementation of
// singleton design pattern
class Singleton
{
private static volatile Singleton obj = null;
private Singleton() {}
public static Singleton getInstance()
{
if (obj == null)
{
// To make thread safe
synchronized (Singleton.class)
{
// check again as multiple threads
// can reach above step
if (obj==null)
obj = new Singleton();
}
}
return obj;
}
}