1.饥饿单例模式的实现:
优点: 饥饿单例,不用使用锁,使用的效率高,同时没有线程安全问题的困扰
缺点: 类加载的时候就要初始化,导致程序启动初始化慢,同时可能后面用不到,但却一直 占着内存,造成浪费
/**
* @author : GONG ZHI QIANG
* @data : 2019-08-31 , 14:27
* @user : SnaChat
* @project: DesignPattern
* @description :实现--饥饿单例模式
*/
public class HungrySingleton {
private static final HungrySingleton singleton = new HungrySingleton();
private HungrySingleton() {
}
private HungrySingleton getInstance() {
return singleton;
}
}
2. 懒汉单例模式
缺点:需要注意到线程安全的问题,可以使用synchronized, 或者ReentrantLock等方式实现线程同步问题
/**
* @author : GONG ZHI QIANG
* @data : 2019-08-31 , 14:33
* @user : SnaChat
* @project: DesignPattern
* @description : 懒汉单例模式
*/
public class LazySingleton {
private static final Object lock = new Object();
private LazySingleton() {
}
private static LazySingleton singleton = null;
/**
* 此处的方法可能出现线程安全的问题,使用 synchronized实现 加锁,但是这样在高并发情况下,又将会出现性能问题,下面将给出一个更优化的方案
* @return
*/
public static LazySingleton getInstance() {
synchronized (lock) {
//这里有一个IDEA快捷键 只有输入 lazy 就有代码块
if (singleton == null) {
singleton = new LazySingleton();
}
return singleton;
}
}
}
3.使用静态内部类 实现单例模式
- 使用静态内部类实现单例模式,当我们不使用单例的时候,单例对象不会创建
- 同时,内部类是在方法调用之前一定要实现初始化,所以这里规避了多线程的不安全访问的问题
- 使用静态内部类的方式同时规避了前两种实现方式的弊端,
/**
* @author : GONG ZHI QIANG
* @data : 2019-08-31 , 14:44
* @user : SnaChat
* @project: DesignPattern
* @description :
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){}
public static final StaticInnerClassSingleton getInstance(){
return StaticInnerClass.SINGLETON;
}
private static class StaticInnerClass{
private static final StaticInnerClassSingleton SINGLETON = new StaticInnerClassSingleton();
}
}
4.使用反射的方式—破坏单例模式
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用反射的方式,创建单例的对象
Class<?> calzz = StaticInnerClassSingleton.class;
Constructor constructor = null;
constructor = calzz.getDeclaredConstructor(null);
constructor.setAccessible(true);
StaticInnerClassSingleton first = (StaticInnerClassSingleton) constructor.newInstance();
StaticInnerClassSingleton second = (StaticInnerClassSingleton) constructor.newInstance();
//此处的输出结果返回的是false,证明单例类实际上创建了两个对象,这不是我们想看到的结果
System.out.println("(second == first) ? = " + (second == first));
}
下面是解决的方案
private StaticInnerClassSingleton(){
if (StaticInnerClass.SINGLETON != null) {
throw new RuntimeException("创建的对象已经存在,不能再次创建爱对象!");
}
}
上面的方式保证了我们使用反射的方式创建单例,也是安全的。
但是,这样就安全了吗? NO!
5.使用序列化的方式–也可能会破坏单例模式
这种情况下,从磁盘中读取出来的单例类,创建对象,可能在内存中存在多个
6、更好的单例模式—使用容器创建(Spring 框架管理Bean)
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> container = new ConcurrentHashMap<>();
public static Object getBean(String beanName) {
synchronized (container) {
if (!container.containsKey(beanName)) {
Object newObject = null;
try {
newObject = Class.forName(beanName).newInstance();
container.put(beanName, newObject);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return newObject;
} else {
return container.get(beanName);
}
}
}
}
上面的例子中的容器实现的单例不是线程安全的
7.每个线程都有一个副本 ,不论每个线程调用调用多少次,都会返回同一个对象,这个就能够保证线程的安全性,这是一种一空间换取时间的方式
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
public ThreadLocalSingleton() {
}
public static ThreadLocalSingleton getInstance(){
return threadLocal.get();
}
}