设计模式-单例

意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

例子

public class World {
private static final World world;

private World() {

}

public static World getInstance() {
if (world == null)
world = new World();
return world;
}
}

这里需要注意的一点就是构造函数使用了访问权限private修饰,表示别的地方无法构建该对象,除了他自己。

另外由于多线程安全问题,这里一般推荐下面的写法:

public class World {
private static final World world;

private World() {

}

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

适用性

  1. 当类只能有一个实例而且客户可以从一个众所周知的访问点访问他
  2. 当这个唯一实例应该是通过子类化可扩展的,而且客户应该无需更改代码就能使用一个扩展的实例

结构

github

协作

客户只能通过Singleton的Instance操作访问一个Singleton的实例。

效果

Singleton模式有许多优点:

  1. 对唯一实例的受控访问。因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及何时访问它。
  2. 缩小名空间。Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染名空间。
  3. 允许对操作和表示的精化。Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。
  4. 允许可变数目的实例。这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,你可以用相同的方法来控制应用所使用的实例的数目。只有允许访问Singleton实例的操作需要改变。
  5. 比类操作更灵活。

几种单例

1.静态参数

public class Signleton {
public static Map<String, String> cache = new HashMap<>();
}

2.懒汉模式(线程不安全)

public class Signleton {
private static Signleton instance;

private Signleton() {}

public static Signleton getInstance() {
if (signleton != null) return instance;
instance = new Signleton();
return instance;
}
}

当需要,就创建;不需要,就为空。

3.懒汉模式(线程安全)

public class Signleton {
private static Signleton instance;

private Signleton() {}

public synchronized static Signleton getInstance() {
if (signleton != null) return instance;
instance = new Signleton();
return instance;
}
}

增加synchronized锁完成现场安全。但是访问都会被锁占用资源。

4.饿汉模式(线程安全)

public class Signleton {
private static Signleton instance = new Signleton();

private Signleton() {}

public static Signleton getInstance() {
return instance;
}
}

可以看到与1初始化一致,因此每次程序在打开时就会初始化,如果初始化过多导致打开卡顿。

5.使用类的内部类(线程安全)

public class Signleton {
private static class SignletonHolder {
private static Signleton instance = new Signleton();
}

private Signleton() {}

public static Signleton getInstance() {
return SignletonHolder.instance;
}
}

仅在需要使用单例时调用getInstance进行单例的创建,同时不会因为加锁而增加性能消耗。
Java 加载外部类的时候,不会创建内部类的实例,只有在外部类使用到内部类的时候才会创建内部类实例。

6.双重锁校验(线程安全)

public class Signleton {
private static Signleton instance;

private Signleton() {}

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

锁上提供了优化。

7.Atomic(线程安全)

Atomic原子类就是利用自旋CAS来保证线程安全的。

public class Signleton {
private static final AtomicReference<Signleton> INSTANCE = new AtomicReference<Signleton>();

private static Signleton instance;

private Signleton() {}

public static Signleton getInstance() {
for ( ; ; ) {
Signleton instance = INSTANCE.get();
if (instance != null) return instance;
INSTANCE.compareAndSet(null, new Signleton());
return INSTANCE.get();
}
}
}

8.Effective Java作者推荐的枚举单例例(线程安全)

public enum Signleton {
INSTANCE;
public void test(){
System.out.println("test");
}
}