经典单例模式
在使用中这个类只有一个实例对象。并提供一个全局访问点。
首先我们先私有该类的构造方法,然后在自己内部new自己,这样就能控制这个类只有一个实例对象。
public class singleTon { private static singleTon st =null;
public static singleTon getInstance() { if(st==null){ st = new singleTon(); } return st;} }private singleTon() { }
上述代码在单线程下是没有任何问题的,但是在多线程的情况下,外部A,B两个线程执行getInstance这个方法,但是线程A在获取自己的st是null时进入if判断,突然B线程获得了cpu的时间片,这时候线程A并没有new出来自己,线程B获得的也是一个空值,于是B线程去new一个对象,等线程B执行完,cpu开始执行线程A,而线程A中的st依然是null,线程A又new了一个对象。很显然在多线程下这种单例模式是有bug的。
单例模式优化
1.同步锁
public class singleTon { private static singleTon st = null;
private singleTon() { } public static synchronized singleTon getInstance() { if(st==null){ st = new singleTon(); } return st; } }
缺点:简单粗暴的解决方法,但是每次调用getInstance都得等待线程同步,对于频繁访问的对象来说太耗资源。
2.直接赋值
public class singleTon { private static singleTon st = new singleTon();
private singleTon() { } public static singleTon getInstance() { if(st==null){ st = new singleTon(); } return st; } }
缺点:即使代码执行过程中没有用到这个对象也会创建这个对象,浪费一些内存资源。
3.双重检查锁
public class singleTon { private volatile static singleTon st = null;
private singleTon() { } public static singleTon getInstance() { if(st==null){ synchronized(singleTon.class){ if(st==null){ st = new singleTon(); } } } return st; } }
这种方法synchronized同步块中的内容只能执行一次,如果代码没有用到这个对象不会new出来新对象,节省了内存和cpu的开销。是最优的解法。