单例模式:

单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。

1.懒汉式单例:

  1. //懒汉式单例类.在第一次调用的时候实例化自己
  2. public class Singleton {
  3. private Singleton() {}
  4. private static Singleton single=null;
  5. //静态工厂方法
  6. public static synchronized Singleton getInstance() {
  7. if (single == null) {
  8. single = new Singleton();
  9. }
  10. return single;
  11. }
  12. //或者这样
  13. public static Singleton getInstance() {
  14. if (singleton == null) {
  15. synchronized (Singleton.class) {
  16. if (singleton == null) {
  17. singleton = new Singleton();
  18. }
  19. }
  20. }
  21. return singleton;
  22. }
  23. }
  24. // 这个饿汉式在多线程的时候,会出现线程安全问题,因此加上synchronized

2.饿汉式单例:

  1. //饿汉式单例类.在类初始化时,已经自行实例化
  2. public class Singleton1 {
  3. private Singleton1() {}
  4. private static final Singleton1 single = new Singleton1();
  5. //静态工厂方法
  6. public static Singleton1 getInstance() {
  7. return single;
  8. }
  9. }

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。


3.双重检查锁

  1. public class Singleton {
  2. private Singleton (){
  3. }
  4. private static volatile Singleton uniqueSingleton ;
  5. public static Singleton getInstance(){
  6. if( null == uniqueSingleton){
  7. synchronized (Singleton.class){
  8. if( null == uniqueSingleton){
  9. uniqueSingleton = new Singleton();
  10. }
  11. }
  12. }
  13. return uniqueSingleton;
  14. }
  15. }

**uniqueInstance = new Singleton(); **
这段代码其实是分为三步执⾏:

  1. 为 uniqueInstance 分配内存空间 。
  2. 初始化 uniqueInstance 。
  3. 将 uniqueInstance 指向分配的内存地址 。

但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1>3>2。指令重排在单线程环境下不会出 现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。