20210828
饿汉模式、懒汉模式
建议使用饿汉,因为简单
饿汉模式
类加载时就创建实例,缺点是假如这个单例类一直不被用到,创建出来的对象就是个浪费
class SingleHungry {
private static final SingleHungry singleHungry = new SingleHungry();
private SingleHungry() {
}
public static SingleHungry getInstance() {
return singleHungry;
}
}
测试代码:
public class SingleHungryTest {
public static void main(String[] args) {
List<String> list = new ArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
String hashcode = SingleHungry.getInstance().hashCode() + "";
if (list.isEmpty()) list.add(hashcode);
if (!list.contains(hashcode)) System.out.println("非单例");
}).start();
}
}
}
懒汉模式
当第一次用到时才创建实例
class SingleLazy {
// volatile是防止singleLazy = new SingleLazy()时指令重排
private volatile static SingleLazy singleLazy;
private SingleLazy() {
MyThreadUtil.sleepSeconds(5);//模拟创建对象时的延时
}
/**
* 2次if判,所以叫DCL:double check lazy
*/
public static SingleLazy getInstance() {
if (singleLazy == null) {
synchronized (SingleLazy.class) {
if (singleLazy == null) singleLazy = new SingleLazy();
}
}
return singleLazy;
}
}
测试代码:
public class SingleLazyTest {
public static void main(String[] args) {
List<String> list = new ArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
String hashcode = SingleLazy.getInstance().hashCode() + "";
if (list.isEmpty()) list.add(hashcode);
if (!list.contains(hashcode)) System.out.println("非单例");
}).start();
}
}
}
为什么要写2次if(DCL)
直接锁整个方法,正确,但是效率低
public synchronized static SingleLazy getInstance() {
if (singleLazy == null) singleLazy = new SingleLazy();
return singleLazy;
}
等同于:
public static SingleLazy getInstance() {
synchronized (SingleLazy.class) {
if (singleLazy == null) singleLazy = new SingleLazy();
}
return singleLazy;
}
上面2种写法的问题在于,当实例已经创建后,并发的线程还是会阻塞,于是改进成:
public static SingleLazy getInstance() {
if (singleLazy == null) {
synchronized (SingleLazy.class) {
if (singleLazy == null) singleLazy = new SingleLazy();
}
}
return singleLazy;
}
为什么要用volatile修饰实例
private volatile static SingleLazy singleLazy;
创建实例的代码singleLazy = new SingleLazy()
对于的字节码指令是3个:
1.创建实例,分配内存
2.初始化实例
3.将singleLazy指向分配的内存
指令重排可能导致字节码的执行顺序是132,当一个线程发现singleLazy!=null,就直接返回他,但是实例还没有初始化完成,这样在使用实例时报错(指令重排成132的概率很小很小很小)
网友评论