滴滴面试官:如何实现一个线程安全的单例模式

单例模式作为最常见的设计模式,有很多实现方式,今天介绍一下单例模式相关的内容。

什么是单例模式

从字面上理解,单例模式需要确保一个类只有一个对象。比如线程池、缓存、日志对象、打印机驱动对象、显卡驱动对象等,这些类的对象往往只需要一个实例就可以。如果一个类的对象需要被频繁创建,那么也会需要频繁GC,单例模式就可以解决这样的问题。

单例模式的实现方式

单例模式的实现方式非常多,但总体上可以分为两类:饿汉式和懒汉式。饿汉式是在类加载过程中就创建对象的方式,而懒汉式是需要使用时才会去创建对象的方式,这两种方式各有特点。饿汉式存在的问题是如果要创建的对象占用的空间非常大,且使用频率非常低,那么这种方式是非常不划算的。而懒汉式有可能会遇到并发问题,这就要求我们需要考虑对创建对象的过程进行加锁。不管是饿汉式还是懒汉式,创建的对象一般都得使用static关键字进行修饰。

饿汉式单例

public class Singleton {
     
	private static Singleton instance = new Singleton();
	private Singleton() {
     }
	public static Singleton getInstance() {
     
		return instance;
	}
}

上面的饿汉式单例在类加载时创建了一个对象,然后创建了一个private修饰的构造方法防止通过构造方法创建对象。

懒汉式单例(非线程安全版)

public class Singleton {
     
	private static Singleton instance;
	private Singleton() {
     }
	public static Singleton getInstance() {
     
		if (instance == null) {
     
			instance = new Singleton();
		}
		return instance;
	}
}

上面这种单例方式的创建我们可以很容易看出当需要使用这个对象时才会去创建,满足懒汉式的要求。但是这种创建单例的方式是有缺陷的,当多个线程同时获取对象,在判断对象是否为空的地方,如果两个线程同时到达,那么就会同时进入这个条件中,创建多个对象。下面提出一些线程安全的懒汉式单例。

懒汉式单例(synchronized修饰的线程安全版)

public class Singleton {
     
	private static Singleton instance;
	private Singleton() {
     }
	public static synchronized Singleton getInstance() {
     
		if (instance == null) {
     
			instance = new Singleton();
		}
		return instance;
	}
}

基于上面的非线程安全版本,我们在getInstance()方法上加上了synchronized锁,就变成了线程安全的版本。但是这种直接在方法上加锁的方式其实对并发的效率影响是很大的,尽管在JDK6中对synchronized做了很大优化,但是仍然不能满足我们对高并发的要求,下面介绍一下更加高效的双重校验锁版。

懒汉式单例(双重校验锁版)

public class Singleton {
     
	private static Singleton instance;
	private Singleton() {
     }
	public static Singleton getInstance() {
     
		if (instance == null) {
     
			synchronized(Singleton.class) {
     
				if (instance == null) {
     
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

我们又基于前面的非线程安全的版本实现了这个双重校验锁版的懒汉式单例,如果多个线程同时在外层判断对象为空时,开始抢占锁资源,抢到的线程创建对象,其他线程就不再需要创建线程了。这种方式的优点在于只有第一次抢夺资源的时候需要进行同步,后续的并发都会判断对象不为空,直接返回对象。

懒汉式单例(静态内部类版)

public class Singleton {
     
	//静态内部类
	private static class SingletonHandler {
     
		private static Singleton instance = new Singleton();
	}
	private Singleton() {
     }
	public static Singleton getInstance() {
     
		return SingletonHandler.instance;
	}
}

该方式依然是懒汉式单例,只不过利用静态内部类实现,同样也是线程安全的。

饿汉式单例(枚举版)

public class EnumSingleton {
     
	private EnumSingleton() {
     }
	public static EnumSingleton getInstance(){
     
        return Singleton.singletonFactory.getInstance();
    }
	private enum Singleton {
     
		singletonFactory;
		private EnumSingleton instance;
		private Singleton() {
     	//枚举类的构造方法在类加载时被实例化
			instance = new EnumSingleton();
		}
		public EnumSingleton getInstance() {
     
			return instance;
		}
	}
}

上面是利用枚举创建单例对象的方法,利用枚举类加载时会实例化构造方法的特点进行单例模式的实现,是一种比较推荐使用的方法。

你可能感兴趣的:(设计模式,设计模式,Java多线程)