单例模式:
单例模式有以下特点:
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 的指令重排,保证在多线程环境下也能正常运⾏。
