简述:本篇文章主要是描述单例的一些关键点
1.构造函数用private
避免被其他类new出一个对象
2.懒汉式存在线程安全问题
public class Singleton2 {
private Singleton2(){
}
private static Singleton2 instance;
public static Singleton2 getInstance(){
if(instance == null) {//1:读取instance的值
instance = new Singleton2();//2: 实例化instance
}
return instance;
}
}
原因:若A线程走到instance = new Singleton2();还没有new对象,B线程也进来,此时将会创建两个对象
解决:
a.在方法前面加synchronized修饰,这样肯定不会再有线程安全问题。
public class Singleton2 {
private Singleton2(){
}
private static Singleton2 instance;
public static synchronized Singleton2 getInstance(){
if(instance == null) {//1
instance = new Singleton2();//2
}
return instance;
}
}
弊端:性能不够,每次执行getInstance都要先获取锁,有点串行化的感觉
b.加同步代码块,缩小粒度,此处可以使用双重检查锁的方式(synchronized代码块外层加一个判断,代码块的内部加一个判断:为什么代码块外部要再检查一次,因为会有多个线程在等待synchronized 代码块的锁,外部加一个判断可以过滤一部分,加快效率)
public static Singleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (Singleton.class) {
if (instance == null) { //Double Checked
instance = new Singleton();
}
}
}
return instance ;
c.指令重排序问题
instance = new Singleton()其实可以分为下面的步骤:
(1).申请一块内存空间;
(2).在这块空间里实例化对象;
(3).instance的引用指向这块空间地址;
问题原因:对于以上步骤,指令重排序很有可能不是按上面123步骤依次执行的,有可能顺序:1.申请空间,3引用指向,2实例化对象;走到3,判断instance==null时,此时已经引用指向了,所以不会创建对象
解决:
加上volatile关键字,因为volatile可以禁止指令重排序。
public class Singleton2 {
private Singleton2(){
}
private static volatile Singleton2 instance;
public static Singleton2 getInstance(){
if(instance == null) {
synchronized (Singleton2.class){
if (instance == null){
instance = new Singleton2();
}
}
}
return instance;
}
}
3.避免被clone,单例类不可以实现cloneable接口,并实现clone方法,因为clone不需要调用构造函数即可复制对象
4.上限的多例模式,即在对象类中放一个list<Object>可以适当提升性能
public class Test{
private static int maxNum=2;//最大对象数
private static Vector<Object> list = new Vector<Object>();
static{
for(int i=0;i<maxNum;i++){
list.add(new Test());
}
}
private Test(){}
}
5.防止反射破坏单例 (定义常量,构造函数调用一次就+1,如果大于0就说明构造函数掉用了第二次,即第二次创建对象了)
public class Singleton {
private static int count = 0;
private static Singleton instance = null;
private Singleton(){
synchronized (Singleton.class) {
if(count > 0){
throw new RuntimeException("创建了两个实例");
}
count++;
}
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
6.使用单例最大的好处在于可以统一管理一个bean的生命周期,决定什么时候进行创建,什么时候销毁例如spring