免费的h5制作网站,外贸公司招聘条件,dw制作网页的基本步骤,网站开发的技术风险单例也就是只能有一个实例#xff0c;即只创建一个实例对象#xff0c;不能有多个。
可能会疑惑#xff0c;那我写代码的时候注意点#xff0c;只new一次不就得了。理论上是可以的#xff0c;但在实际中很难实现#xff0c;因为你无法预料到后面是否会脑抽一下~~因此我们…单例也就是只能有一个实例即只创建一个实例对象不能有多个。
可能会疑惑那我写代码的时候注意点只new一次不就得了。理论上是可以的但在实际中很难实现因为你无法预料到后面是否会脑抽一下~~因此我们就可以通过编码技巧让编译器来帮助我们检查约束。
单例模式又分为两种饿汉式和懒汉式区别在于实例对象创建的时机不同。
0x00 饿汉式
说到饿汉那一定是很饥饿的人很急切也就是说这个实例对象创建的时机比较早在类加载的时候就创建了。
要点 1、由于创建时机较早设置为静态即可在类加载的时候就可以创建实例对象 2、由于只能创建一个实例对象因此将构造函数私有化 3、对外提供一个获取实例对象的静态类方法 public class Singleton{private static Singleton instance new Singleton();public static Singleton getInstance(){return instance;}private Singleton(){}
}
0x01 懒汉式
懒汉顾名思义很懒比较缓慢也就是说这个实例对象是在使用到的时候才去创建。
要点 1、一开始设置为null直到在使用的时候再去new实例对象 2、由于只能创建一个实例对象因此将构造函数私有化
3、对外提供一个获取实例对象的静态类方法
public class SingletonLazy {private static SingletonLazy instance null;public static SingletonLazy getInstance(){if(instance null){instance new SingletonLazy();}return instance;}private SingletonLazy(){}
}
0x02 单例模式与多线程
众所周知许多在单线程环境下的代码一到多线程环境下就会出错在此将对单例模式在多线程下的编写进行调整优化~
先来看看饿汉式的代码我们发现这个实现的方式是在类加载的时候就去实例化对象了而后续在多线程中掉用getInstance方法来获取实例对象的时候只会进行返回操作即”只读”因此多个线程在操作的时候是安全的。
再来看看懒汉式可以发现在调用getInstance方法的时候会进行修改操作如果恰巧有两个线程同时进入了if然后就会进行new操作那不就创建了多个实例对象了如果恰巧每个对象不是很轻量可能有很多属性加载了几十G的数据到内存中...... 因此解决方法是进行加锁操作保证if和new操作是原子的。
public class SingletonLazy {private static SingletonLazy instance null;public static SingletonLazy getInstance(){synchronized(SingletonLazy.class){if(instance null){instance new SingletonLazy();}}return instance;}private SingletonLazy(){}
}
这样修改以后就能保证if和new操作是一个整体此时线程的安全问题就得到了改善。
但是这样写是否就真的完了呢
答案不是。因为我们是单例模式所以只需要new一次之后实例对象就不可能是null了后续直接return对象就行了。假如我们现在已经创建好了一个对象当后续有多个线程去调用getInstance方法的时候需要去获取到这把锁才能进行返回操作也就是我们的多线程变成了串行化并发程度几乎没了~
那是否有办法既可以保证代码线程安全又不会对执行效率产生大的影响呢
如下代码
public class SingletonLazy {private static SingletonLazy instance null;public static SingletonLazy getInstance(){if(instance null){synchronized(SingletonLazy.class){if(instance null){instance new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}
与前面的代码对比可以发现在外层多加了一个if语句。
分析
当有多个线程进入了第一个if语句此时里面有一个加锁操作这样可以保证只创建了一个实例对象。假设此时已经创建好了一个实例对象了当下一次又有多个线程进来时由于instance不为null直接返回了并不会进入到加锁操作此时并发并没有再受到影响了既保证了正确又保证了效率。
总结
在锁外面的if语句保证了是否要进行加锁操作即如果还没有创建实例对象此时线程由于需要进行修改操作要进行加锁才能保证安全如果已经创建了实例对象此时线程安全了就不用加锁了。
在锁里面的if语句是来保证只创建一个实例对象。
0x03 指令重排序
上述代码其实还不够安全还存在指令重排序的问题。
指令重排序本质是编译器为了提高执行效率调整原有的代码执行顺序(前提是保持逻辑不变)。
接下来具体问题具体分析
上述的代码中出现了一个new操作而new操作其实是分三步的第一步申请内存空间第二步在内存空间上构造对象第三步把内存地址赋值给对象引用。一般是按照1,2,3的顺序执行的但是编译器优化过后可能会按照1,3,2的顺序执行在单线程环境下都可以但是在多线程环境下则不是。如果线程1是按照1,3,2顺序执行完了3后线程2此时返回了这个实例对象但是此时这个对象还没有初始化呢也就是说这是个非法对象当我们的线程2去访问这个对象的属性或方法时此时就是非法会出错。此时我们还需要再对instance加一个限制操作即用volatile来修饰instance。
最终代码
public class SingletonLazy {private static volatile SingletonLazy instance null;public static SingletonLazy getInstance(){if(instance null){synchronized(SingletonLazy.class){if(instance null){instance new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}
至此这段“健壮”的代码基本可以满足使用了~