单例模式
单例
1.饿汉式单例
优点:没有加锁,执行效率更高
缺点:类加载的时候就初始化,不管用否都占着空间,如果项目中存在大量的单例对象,则会浪费内存空间
2.懒汉式单例
优点:被外部类调用时,内部才会加载
存在线程安全,当有多个线程同时访问时,返回多个实例,违反了单例对象原则
添加了synchronized关键字,当有大量线程同时访问时,cpu分配压力上升,造成阻塞,造成性能下降
双重检查锁
这里的写法将同步放在了方法里面的第一个非空判断之后,这样可以确保对象不为空的时候不会被阻塞,但是第二个非空判断的意义是什么呢?我们假设线程A首先获得锁,进入了第3行,还没有释放锁的时候,线程B又进来了,这时候因为线程还没有执行对象初 始化,所以判空成立,会进入第2行等待获得锁,这时候当线程A释放锁之后,线程B会进入到第3行,这时候因为第二个判空判断对象不为空了,所以就会直接返回,如果没有第2个判空,这时候就会产生新的对象了,所以需要两次判空!
大家可能注意到这里的变量定义上加了volatile关键字,为什么呢?这是因为DCL在可能会存在失效的情况:
第4行代码:lazySingleton = new LazyDoubleCheckSingleton();
大致存在以下三步:
(1)、分配内存给对象
(2)、初始化对象
(3)、将初始化好的对象和内存地址建立关联(赋值)
而这3步由于CPU指令重排序,不能保证一定按顺序执行,假如线程A正在执行new的操作,第1步和第3步都执行完了,但是第2步还没执行完,这时候线程B进入到方法中的第1行代码,判空不成立,所以直接返回了对象,而这时候对象并没有初始化完全,所以就会报错了,解决这个问题的办法就是使用volatile关键字,禁止指令重排序(jdk1.5之后),保证按顺序执行上面的三个步骤
使用了内部类的特性,LazyHolder里面的内容等到外面调用时才执行,可以被反射或者序列化破坏单例
3.注册式单例
注册式单例是将所有的对象保存到某一个地方,通过唯一的标识符获取对应的单例对象
4.ThreadLacal式单例
ThreadLacal不能保证创建对象的全局唯一性,但是可以保证单个线程中对象的唯一性