初始阶段就完成对象的实例化,并保持一种饥饿渴求的状态。无论此单例是否有人使用。
缺点:没人用就占用内存。
public class Sun {
private static final Sun sun = new Sun();
private Sun() {
}
public Sun getInstance() {
return sun;
}
}
解决饿汉式有可能造成内存浪费的问题。
public class Sun {
private static Sun sun;
private Sun() {
}
public static Sun getInstance() {
if (sun == null) { //有可能多个线程逻辑判断同时成立
sun = new Sun();
}
return sun;
}
}
懒汉式在第一次获取对象时比饿汉式要慢。
并且有线程安全问题。
public class Sun {
private static Sun sun;
private Sun() {
}
//加上了同步锁,所有线程一个一个获取实例
public static synchronized Sun getInstance() {
if (sun == null) {
sun = new Sun();
}
return sun;
}
}
上述加锁的范围很大,锁有点重。
public class Sun {
// volatile防止指令重排序
private volatile static Sun sun;
private Sun() {
}
public static Sun getInstance() {
if (sun == null) {
synchronized (Sun.class){
if(sun==null){ // 避免多次创建对象
sun = new Sun();
}
}
}
return sun;
}
}
- 如果对象已经创建了,后面再次调用直接返回单例对象即可。
- sun = new Sun ();可以分解为:分配内存、初始化对象、指向分配内存地址三个步骤。有可能重排序为分配内存、指向分配内存地址,最后初始化对象。那么其它线程可能获取到一个未初始化的对象。