建网站外包,wordpress 便利贴,北京注册公司查询,免费网页加速器#x1f490;专栏导读 本篇文章收录于多线程#xff0c;也欢迎翻阅博主的其他文章#xff0c;可能也会让你有不一样的收获#x1f604; #x1f337;JavaSE #x1f342;多线程 #x1f33e;数据结构 文章目录 #x1f490;专栏导读#x1f4a1;饿汉模式#x1f4a1;…专栏导读 本篇文章收录于多线程也欢迎翻阅博主的其他文章可能也会让你有不一样的收获 JavaSE 多线程 数据结构 文章目录 专栏导读饿汉模式懒汉模式懒汉模式多线程版volatile防止指令重排序 单例模式是一种经典的设计模式了它的作用就是保证在有些场景下
需要一个类只能有一个对象而不能有多个对象比如像你以后娶媳妇你娶媳妇肯定是只能娶一个而不能娶两个 但是问题来了一个类只需要一个对象那在new对象的时候只new一次对象不就可以了么为什么还要弄个这么麻烦的东西呢
因为啊只new一次对象确实是只有一个但是呢如果你在写代码的过程中忘了呢然后又new了一次这种概率是很大的毕竟人是最不靠谱的动物就像是有一句话说的好宁可相信世界上有鬼也不要相信男人的那张嘴所以的为了防止这种失误发生就有了单例模式在Java中也有许多类似的机制比如final就会保证修饰的变量肯定是不能改变的override保证你这方法肯定是一个重写方法**interface**保证肯定要重写接口里面的方法这些都是在语法方面进行了一些限制但是在语法方面对于单例并没有特定的语法所以这里就通过编程技巧来达到类似的限制效果
单例模式的两种实现方式
饿汉模式
1.在类中实例化类的对象给外界提供一个方法来使用这个对象;
2.将构造方法用private修饰保证在类外不能再实例化这个类对象
public class SingleTon {//在类的内部实例化对象public static SingleTon instance new SingleTon();//定义一个方法用来获取这个对象//后序如果类外的代码想要使用对象时直接调用这个方法即可public static SingleTon getInstance() {return instance;}//设置一个私有的构造方法保证在这个类外无法实例化这个对象private SingleTon(){}
}可以看到这里的对象被static修饰所以在类被加载的时候创建创建的时机就比较早并且被static修饰的对象只会被创建一次所以这种在类加载时就创建实例的模式称为饿汉模式
懒汉模式
懒汉模式单线程版
这样的写法与上面的相同点就是同样在类外不能再第二次实例化对象不同点是将创建对象的时机放在getInstance方法中这样在类加载的时候就不会创造实例而是当第一次调用这个方法时才会去创建
public class SingleTon {public static SingleTon instance null;//定义一个方法用来获取这个对象//后序如果类外的代码想要使用对象时直接调用这个方法即可public static SingleTon getInstance() {//懒汉模式if(instance null) {instance new SingleTon();}return instance;}//设置一个私有的构造方法保证在这个类外无法实例化这个对象private SingleTon(){}
}懒汉模式多线程版
在线程安全方面上面的饿汉模式是线程安全的而懒汉模式在多线程下是不安全的
因为如果多个线程同时访问一个变量那么不会出现不安全问题如果多个线程同时修改一个变量就有可能出现不安全问题
饿汉模式下只进行了访问没有涉及到修改 懒汉模式下不仅进行了访问还涉及了修改那么下面就讲解以下懒汉模式在多线程下如何会产生不安全 既然出现了不安全问题那么如何将懒汉模式修改成安全的呢
方法进行加锁使线程安全 但是如果锁加在这个地方仍然是不安全的因为这样还是会进行穿插执行并没有保证它是一个整体(f非原子性) 并不是加了锁就安全只有锁加对了才会安全在加锁的时候要保证以下几方面
1.锁的 {} 的范围是合理的能够把需要作为整体的每个部分都包括进去
2.锁的对象能够起到锁竞争的效果
懒汉模式多线程版改进
将if语句和new都放在锁里面称为一个整体这样就避免了会穿插执行 public static SingleTon getInstance() {synchronized (SingleTon.class) {if(instance null) {instance new SingleTon();}}return instance;}但是上述代码还有一个问题每当调用getInstance时都会尝试去进行加锁而加锁是一个开销很大的操作而这里的懒汉模式之所以会出现线程不安全问题是因为只是在在第一次调用getInstance方法进行new对象时可能会出现问题但是只要new完对象以后就不用再进行锁竞争再去进行if判断了直接访问就可以了所以再次进行优化 public static SingleTon getInstance() {//在最外面在进行一次判断if(instance null) {synchronized (SingleTon.class) {if(instance null) {instance new SingleTon();}}}return instance;}在第一次实例化对象后以后再调用个getInstance方法时就不会再创建对象而且也不会再去获取锁因为第一个if判断语句都不会进去所以不会执行到加锁的语句
上面的单例模式看着好像是完全没问题了但是还是有一个问题就是可能会触发指令重排序问题所以就需要使用volatile解决指令重排序问题
volatile防止指令重排序
指令重排序编译器会保证在你代码逻辑不变的情况下对代码进行优化使代码的性能得到提高这样的操作称为指令重排序
举个例子 在代码中在实例化对象这一步可能会出现指令重排序问题下面就来讲解一下为什么 对于上述的指令重排序问题解决方案就是使用volatile关键字修饰singleTon
**线程安全的单例模式(懒汉模式)**
public class SingleTon {//使用volatile关键字修饰防止指令重排序public static volatile SingleTon singleTon null;public static SingleTon getSingleTon() {if(singleTon null) {synchronized (SingleTon.class) {if(singleTon null) {singleTon new SingleTon();}}}return singleTon;}private SingleTon() {};}这里再次提醒使用单例模式要注意三个要点
加锁两层if判断使用volatile修饰引用防止指令重排序