单例模式 (懒汉,饿汉,双重校验锁)
单例模式
什么是单例模式?
单例保证一个对象JVM中只能有一个实例,常见单例懒汉式、饿汉式
什么是懒汉式,就是需要的才会去实例化,线程不安全。
什么是饿汉式,就是当class文件被加载的时候,初始化,天生线程安全。
懒汉式 代码
package designpatterns23.singleton; /** * 单例模式的作用:保证一个类在项目中有且只能有一个实例 * 在调用的时候在去初始化天生线程是不安全的 * Created by lizhen on 2018/1/30. */ public class Singleton { private static Singleton singleton; /** * 构造方法私有化,防止在外部进行new */ private Singleton() { } /** * 此方法是线程不安全的 * 提供一个对外的方法获取singleton的实例 * * @return singleton实例 */ public static Singleton getSingleton() { if (null == singleton) { singleton = new Singleton(); } return singleton; } }
饿汉式线程 安全代码
package designpatterns23.singleton; /** * 单例模式的作用:保证一个类在项目中有且只能有一个实例 * 在调用的时候在去初始化天生线程是不安全的 * Created by lizhen on 2018/1/30. */ public class Singleton { private static Singleton singleton; /** * 构造方法私有化,防止在外部进行new */ private Singleton() { } /** * 懒汉式修改为线程安全的方法 * 缺点:加入锁影响效率 * * @return */ public static Singleton getSingleton2() { if (null == singleton) { //第一次同时进入判断的有可能有两个线程,所以在此处加入同步锁,相对于在方法上加入锁,提高了效率 synchronized (Singleton.class) { singleton = new Singleton(); } } return singleton; } }
饿汉式 代码
package designpatterns23.singleton; /** * 单例模式之饿汉式 * 在加载的时候就已经初始化了,天生线程安全 * Created by lizhen on 2018/1/30. */ public class Singleton1 { /** * 利用静态的关键字,类一加载便去初始化此类 */ private static Singleton1 singleton1 = new Singleton1(); /** * 构造方法私有化 */ private Singleton1 (){ } /** * 提供一个对外获取此类的方法 * @return 此类的实例 */ public static Singleton1 getSingleton1(){ return singleton1; } }
双重校验锁
关于volatile关键字的使用,我有一篇专门关于讲解这个关键字的博客以及关于java内存模型的知识,大家可以看一下这一篇
http://mp.blog.****.net/postedit/79043326
package designpatterns23.singleton; /** * 单例模式的作用:保证一个类在项目中有且只能有一个实例 * 单例模式之懒汉式 ----双重校验锁 * 在调用的时候在去初始化天生线程是不安全的 * Created by lizhen on 2018/1/30. */ public class Singleton { /** * 构造方法私有化,防止在外部进行new */ private Singleton() { } /** * 一般我们用双重校验锁的时候需要使用volatile关键字 * volatile 是关于java内存模型相关的知识,只能保证可见性,但不能保证原子性 */
private volatile static Singleton singleton;/** * 双重校验锁模式 * * @return singleton 实例; */ public static Singleton getSingleton3() { if (singleton == null) { synchronized (Singleton.class) { /** * 为什么还要重新判断一下呢? * 因为同时等待锁的有可能有多个线程,当第一线程释放锁之后,后面的线程得到锁就可以进入这个方法 * 这时如果不进行判断的话很有可能就会创建多个实例. */ if (singleton == null) { singleton = new Singleton(); } } } return singleton; }}
我们看一下效果