单例模式
一、定义
- 确保一个类在任何情况下都只有一个实例,且提供一个全局访问点
- 隐藏所有的构造方法(拒绝外部类初始化保证只有一个实例)
- 属于创建者模式
- 使用场景:ServletContext、ServletConfig、ApplicationContext、DBPool
二、写法
- 饿汉式
- 懒汉式
- 注册式
- ThreadLocal单例
饿汉式
- 优点:效率高、性能高、不需要锁(jvm加载机制只会加载一次)
- 缺点:可能会造成内存浪费
懒汉式
- 优点:节省内存
- 缺点:线程不安全(很可能调用的时候返回不同的实例,所以要加锁)
//同步锁
//第一次使用getInstance()需要同步,后面getInstance()不需要同步;每次同步,效率很低
public class SingleTon(){
private SingleTon(){
}
private static SingleTon singleton = null;
public synchronized static getInstance(){
if(singleton == null){
singleton = new SingleTon();
}
return singleton ;
}
}
原文链接:https://blog.csdn.net/jike11231/article/details/106229415/
//双重锁
//安全且在多线程情况下能保持高性能。
//实例变量需要加volatile 关键字保证易变可见性
public class SingleTon4{
private SingleTon4(){
}
private volatile static SingleTon4 singleton4 = null;
public static SingleTon4 getSingleton(){
//是否要阻塞
if(singleton4 == null){
synchronized (SingleTon4.class){
//是否要创建
if(singleton4 == null){
singleton4 = new SingleTon4();
//指令重排序问题,所以用volatile
}
}
}
return singleton4 ;
}
}
原文链接:https://blog.csdn.net/jike11231/article/details/106229415/
//静态内部类模式
//利用了JVM类加载机制来保证初始化实例对象时只有一个线程,
//静态内部类SingletonHolder类只有第一次调用getInstance方法时,才会装载从而实例化对象
//会被反射破坏
public class Singleton5{
private SingleTon5 (){
}
private static class SingletonHolder{
private static final Singleton5 = new Singleton5();
}
public static final Singleton5 getInstance() {
return SingletonHolder.Singleton5 ;
}
}
//反射破坏
public class Test {
public static void main(String args[]) {
private Singleton() {};
Singleton singleton = Singleton.getInstance();
try {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);//强制
Singleton singletonnew = constructor.newInstance();
System.out.println(singleton == singletonnew);
//输出结果为 false
} catch (Exception e) {
}
}
}
//解决方案:可以给构造函数加上判断,限制创建多个实例
private Singleton() {
if (null != Singleton.singleton) {
throw new RuntimeException();
}
}
原文链接:https://blog.csdn.net/jike11231/article/details/106229415/
注册式单例
-
将每一个实例缓存到同意容器中,使用唯一标志获取实例
-
线程不安全
在源码里的应用
1.AbstractFactoryBean:里的getObject方法
2.ErrorContext:错误日志