单例模式,我们或多或少的听过或者用过,就是确保某一个对象只有一个实例,我们先看下一般的写法:
package com.ck.single;
public class Champion {
private static final Champion champion = new Champion();
private Champion() {
}
public static Champion getInstance() {
return champion;
}
}
这样写有一个缺点,就是在项目启动时Champion就已经在JVM中产生对象了,如果单例模式的类比较多,那是很占内存的,这种写法称为饿汉式,还有另外一种成为懒汉式,就是在使用的时候初始化对象。
下面我稍微修改下:
package com.ck.single;
public class Champion {
private static Champion champion = null;
private Champion() {
}
public static Champion getInstance() {
if(champion == null) {
champion = new Champion();
}
return champion;
}
}
这样写虽然缓解了对象的延迟创建,在低并发的情况下可能不会出现问题,但是在高并发情况下可能会出现多个对象,这就违背了单例,什么原因呢?比如两个线程同时进入champion == null,结果判断条件都为真,那么两个线程 则产生了两个对象。
为了保证线程安全,我们引入Synchronized实现单例,我们修改代码:
package com.ck.single;
public class Champion {
private static Champion champion = null;
private Champion() {
}
public synchronized static Champion getInstance() {
if(champion == null) {
champion = new Champion();
}
return champion;
}
}
上面代码的synchronized作用域是静态方法,所有属于类锁,这个我们前面已经讲过,虽然加锁了,但是在并发情况下所有线程都要同步等待,这样即使对象存在了,所有线程也要等待,所以我们需要再优化下:
package com.ck.single;
public class Champion {
private static volatile Champion champion = null;
private Champion() {
}
public static Champion getInstance() {
if(champion == null) {
synchronized(Champion.class) {
if(champion == null) {
champion = new Champion();
}
}
}
return champion;
}
}
改完后的程序,我们会发现虽然两次判断空的情况,但是提升了代码性能,因为如果对象已经存在的话就不需要等待了,而是直接返回。