是什么?
单例指一个类确保只有一个实例。就加载出一个,谁都用这一个。
实现方式
饿汉式-饿了,早点吃。类加载时就实例化。
实现方式就下面这样:
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() { }
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
懒汉式-为啥说它懒呢,是这样的,你要的时候它才造一个给你,延迟加载的。你不要,就不会实例化,有点好处就是省资源。
实现方式就下面这样:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
问题来了
线程安全吗?这么简单就能线程安全,怎么也得多写一点。
好,给方法加锁。改进一下。
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。
那么再想一个办法,缩小加锁的范围。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
} } }
return uniqueInstance;
}}
双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:
1.为 uniqueInstance 分配内存空间
2.初始化 uniqueInstance
3.将 uniqueInstance 指向分配的内存地址
理论是这样,实际上,多线程的时候,这个就不一定按这个顺序了。
volatile的作用就来了。加上这个volatile禁止JVM重排序,就按上面这个进行了。
ok,大概就这么回事。
参考地址:CS-Notes 这个写真的很Nice