Hexsmith's blog

业精于勤荒于嬉,行成于思毁于随!

0%

design-pattern-singleton

1.单例模式

  • 单例模式限制了一个类的实例化,并确保java虚拟机中只存在一个类的实例。
  • 单例类必须提供一个全局访问点来获取类的实例。
  • 单例模式用于日志记录,驱动程序对象,缓存和线程池。
  • Singleton设计模式也用于其他设计模式,如Abstract Factory,Builder,Prototype,Facade等。

2.要点

  • 私有的构造函数
  • 公共静态方法,返回类的实例,这是外部世界获取单例类的实例的全局访问点。

3.饿汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description:饿汉式单例
* @author hexsmith
* @date 2017年11月6日 下午6:36:41
* @version v1.0
*/
public class EagerInitializedSingleton {

private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

private EagerInitializedSingleton(){}

public static EagerInitializedSingleton getInstance(){

return instance;
}
}

这种在初始化时,Singleton类的实例是在类加载时创建的,这是创建单例类的最简单的方法,但它有一个缺点即使客户端应用程序可能没有使用它也会创建实例。

4.懒汉式单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description:懒汉式单例
* @author hexsmith
* @date 2017年11月6日 下午6:47:10
* @version v1.0
*/
public class LazyInitializedSingleton {

private static LazyInitializedSingleton instance;

private LazyInitializedSingleton(){}

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

}

上面的实现对单线程环境工作良好,但是当涉及到多线程系统时,如果多个线程同时处于if循环内部,则会导致问题。它会破坏单例模式,两个线程都会得到单例类的不同实例。

5.静态代码块实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description:使用static代码块实现单例
* @author hexsmith
* @date 2017年11月6日 下午6:41:15
* @version v1.0
*/
public class StaticBlockSingleton {

private static StaticBlockSingleton instance;

private StaticBlockSingleton(){}

static{
try{
instance = new StaticBlockSingleton();
}catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance");
}
}

public static StaticBlockSingleton getInstance(){
return instance;
}

}

6.静态内部类实现单例

在Java 5之前,Java内存模型存在很多问题,以上方法在某些情况下失败,因为太多的线程试图同时获得Singleton类的实例。所以Bill Pugh提出了一种使用内部静态帮助类来创建Singleton类的另一种方法。Bill Pugh Singleton的实现是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description 静态内部类实现单例
* @author hexsmith
* @date 2018年5月9日 下午3:44:11
* @version v1.0
*/
public class BillPughSingleton {

private BillPughSingleton() {}

private static class singletonHelper {
private static final BillPughSingleton instance = new BillPughSingleton();
}

public static BillPughSingleton getInstance() {
return singletonHelper.instance;
}

}

注意包含单例类的实例的私有内部静态类。加载单例类时,SingletonHelper类不会加载到内存中,只有当有人调用getInstance方法时,才会加载此类,并创建Singleton类实例。这是Singleton类最广泛使用的方法,因为它不需要同步。

7.线程安全的单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description:线程安全的单例
* @author hexsmith
* @date 2017年11月6日 下午6:50:00
* @version v1.0
*/
public class ThreadSafeSingleton {

private static ThreadSafeSingleton instance;

private ThreadSafeSingleton() {
}

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

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

这种方式实现了线程安全的单例模式,但是在高并发场合使用时性能很糟糕。所以不推荐在高并发环境中使用

8.单例模式序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.hexsmith.design.pattern.creational.singleton;

import java.io.Serializable;

/**
* @Description 单例模式序列化
* @author hexsmith
* @date 2018年5月9日 下午4:16:19
* @version v1.0
*/
public class SerializedSingleton implements Serializable {

/**
* 序列号
*/
private static final long serialVersionUID = -7308821136820448697L;

private SerializedSingleton(){}

private static class SingletonHelper{
private static final SerializedSingleton instance = new SerializedSingleton();
}

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

public static SerializedSingleton getInstance(){
return SingletonHelper.instance;
}

}

9.使用反射破坏单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.hexsmith.design.pattern.creational.singleton;

import java.lang.reflect.Constructor;

/**
* @Description 使用反射破坏单例模式
* @author hexsmith
* @date 2018年5月9日 下午4:01:41
* @version v1.0
*/
public class ReflectionSingletonTest {

@SuppressWarnings("rawtypes")
public static void main(String[] args) {
EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance();
EagerInitializedSingleton instanceTwo = null;
try {
Constructor[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors();
for(Constructor constructor : constructors) {
// Below code will destroy the singleton pattern
constructor.setAccessible(true);
instanceTwo = (EagerInitializedSingleton)constructor.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(instanceOne.hashCode());
System.out.println(instanceTwo.hashCode());
}

}

10.枚举单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.hexsmith.design.pattern.creational.singleton;

/**
* @Description 枚举方式的单例
* @author hexsmith
* @date 2018年5月9日 下午3:58:33
* @version v1.0
*/
public enum EnumSingleton {

INSTANCE;

public static void doSomething() {
// do something
}
}

为了克服Reflection的这种情况,Joshua Bloch建议使用Enum来实现Singleton设计模式,因为Java确保任何枚举值在Java程序中仅实例化一次。由于Java Enum值是全局访问的,单例也是全局可访问的。缺点是枚举类型有点不灵活; 例如,它不允许延迟初始化

11.参考资料

------------- 本文结束 感谢您的阅读 -------------