单例模式:
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
1.懒汉式单例:
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
//或者这样
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
// 这个饿汉式在多线程的时候,会出现线程安全问题,因此加上synchronized
2.饿汉式单例:
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
3.双重检查锁
public class Singleton {
private Singleton (){
}
private static volatile Singleton uniqueSingleton ;
public static Singleton getInstance(){
if( null == uniqueSingleton){
synchronized (Singleton.class){
if( null == uniqueSingleton){
uniqueSingleton = new Singleton();
}
}
}
return uniqueSingleton;
}
}
**uniqueInstance = new Singleton(); **
这段代码其实是分为三步执⾏:
- 为 uniqueInstance 分配内存空间 。
- 初始化 uniqueInstance 。
- 将 uniqueInstance 指向分配的内存地址 。
但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1>3>2。指令重排在单线程环境下不会出 现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1 和 3,此时 T2 调⽤ getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 使⽤ volatile 可以禁⽌ JVM 的指令重排,保证在多线程环境下也能正常运⾏。