单例模式:饿汉模式以及懒模式
饿汉模式:线程安全,不会产生多个实例。但如果加载耗时,增加项目初始化时间,如果在运行过程中,没有用到,可能增加空间的浪费
懒汉模式:充分利用资源,实现懒加载策略。(可能线程不安全,需要用户自己优化)
例子
饿汉模式
/**
* Description:饿汉模式
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 3:52
*/
public class HungrySingleton {
/**
* 利用classLoader加载
*/
private static final HungrySingleton hungrySingleton = new HungrySingleton();
/**
* 私有的构造函数:不让外界创建
*/
private HungrySingleton() {
}
//提供唯一对外,反射除外
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
懒汉模式:几种变形
1.不加任何修饰(不安全)
/**
* Description:懒汉模式
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 3:58
*/
public class LazySingleton {
/**
* 未初始化
*/
private static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
//第一次调用会初始化.不过多线程情况下,会重复初始化(线程安全问题)
if (instance == null) instance = new LazySingleton();
return instance;
}
}
2.加synchronized(不安全)
/**
* Description:懒汉模式
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 3:58
*/
public class LazySingleton1 {
/**
* 未初始化
*/
private static LazySingleton1 instance;
private LazySingleton1() {
}
public static LazySingleton1 getInstance() {
if (instance == null) {
//但是如果线程A和B同时执行到了Synchronized(singleton.class),
// 虽然也是只有一个线程能够执行,假如线程B先执行,线程B获得锁,线程B执行完之后,线程A获得锁,
// 但是此时没有检查singleton是否为空就直接执行了,所以还会出现两个singleton实例的情况
synchronized(LazySingleton1.class)
{
instance = new LazySingleton1();
}
}
return instance;
}
}
2. 加synchronized以及判断是否NULL( 不安全)
/** * Description:懒汉模式:双重检查 * User: lc * Date: 2017/9/30 0030 * Time: 下午 3:58 */ public class LazySingleton2 { /** * 要volatile修饰 * 理由:比如2个线程A/B线程。 */ private static LazySingleton2 instance; private LazySingleton2() { } public static LazySingleton2 getInstance() { if (instance == null) { synchronized (LazySingleton2.class) { if(instance == null) //此处还是有问题:虽然做了双重判断。 因为 newLazySingleTon1()是非原子操作 // A a=new A() // 进行了三步操作:1.申请空间2.将引用指向该空间地址3.初始化构造 //在理想情况下,按照1->3->2的步骤执行时,双检锁形式可以正常工作,但是由于Java内存模型,允许重排序,所以 //完全可能按照1->2->3的顺序执行,则导致双检锁形式出现问题。即线程1在执行single=new Singleton()语句时,刚好按照1->2->3的顺序 //执行到step2处,此时,instance指向mem内存区域,而该内存区域未被初始化;同时,线程2在第一个if(instance==null)地方发现instan // ce不为null了,于是得到这个为被初始化的实例instance进行使用,导致错误。
//synchronized:允许修饰的对象,可见性,但是可以允许jvm的进行重排序 instance = new LazySingleton2(); return instance; } } return instance; } }
3.加synchronized以及volatile(安全)/** * Description:懒汉模式:双重检查 * User: lc * Date: 2017/9/30 0030 * Time: 下午 3:58 */ public class LazySingleton2 { /** * 要volatile修饰 * 理由:比如2个线程A/B线程。 */ private volatile static LazySingleton2 instance; private LazySingleton2() { } public static LazySingleton2 getInstance() { if (instance == null) { synchronized (LazySingleton2.class) { if (instance == null) instance = new LazySingleton2(); } } return instance; } }
4.静态内部类(安全)/** * Description:懒汉模式: * User: lc * Date: 2017/9/30 0030 * Time: 下午 3:58 */ public class StaticLazySingleton { /** * *1.私有内部类,外部不知道该类的存在 * 2.外部类初始化的时候,该内部类还没加载,只有通过第一次调用才(比如这个例子:触发getInstance())加载,加载是利用jvm加载,而jvm加载某个类,只能加载一次,所以是只能有一个class类。故线程安全 * 3.对外提供唯一的出口 * */ private StaticLazySingleton() { } // 静态内部类 private static class StaticLazySingletonInstance { private static final StaticLazySingleton staticLazySingleton = new StaticLazySingleton(); } public static StaticLazySingleton getInstance() { return StaticLazySingletonInstance.staticLazySingleton; } }