化州 网站建设,厦门网站设计公司找哪家福建小程序开发,宣传片拍摄要求,丽江网站建设前言
本篇介绍的是wait与notify方法#xff0c;通过wait来顺序控制执行一些代码#xff0c;了解单例模式#xff0c;进行单例模式的简单实现#xff0c;介绍饿汉模式下出现线程不安全的问题与解决#xff1b;如有错误#xff0c;请在评论区指正#xff0c;让我们一起交…
前言
本篇介绍的是wait与notify方法通过wait来顺序控制执行一些代码了解单例模式进行单例模式的简单实现介绍饿汉模式下出现线程不安全的问题与解决如有错误请在评论区指正让我们一起交流共同进步 文章目录前言1. 认识 wait 与 notify1.1 wait,notify的使用1.2 使用 wait 必须加锁1.3 notify 也需要加锁1.4 wait 与 sleep 的区别2.单例模式2.1 单例模式的介绍2.2 java语法中如何实现单例模式2.3 实现单例模式2.3.1 饿汉模式实现单例2.3.2 懒汉模式实现单例模式2.3.3 懒汉模式下产生的线程不安全2.3.4 小结懒汉模式下产生的线程不安全总结本文开始1. 认识 wait 与 notify
为什么要有wait 与 notify ? 线程调度是无序的但是一定场景下希望线程是有序执行的这就可以使用wait 和 notify;
之前了解了join能够等待来控制顺序但是其功效有限而wait也是等待的作用但又不相同
1.1 wait,notify的使用
wait: 发现条件不满足 / 时机不成熟就会阻塞等待 notify: 其他线程构造了一个成熟的条件就可以唤醒等待线程
例如 A去ATM取钱A进去取钱发现ATM中没有钱A就会wait释放锁并阻塞等待当另外的线程把ATM冲上钱就可以唤醒A让A进行取钱操作
wait与notify前提使用条件 wait 和 notify 是Object 方法只要是类对象就可以使用wait,notify;
1.2 使用 wait 必须加锁
不加锁代码 public static void main(String[] args) throws InterruptedException {Object object new Object();object.wait();//中断就会报异常}结果报错 【注】illegal Monitor State Exception: 非法的监视器(synchronized: 监视器锁)状态异常》非法的锁状态异常
object.wait() : wait方法做三件事 ① 解锁 这就说明使用wait必须加锁必须写的synchronized代码块中才能解锁 【注】加锁对象必须和wait的对象是同一个 ② 阻塞等待 ③ 当收到通知的时候就唤醒同时尝试重新获取锁
join 与 wait 的区别 前提有两个线程t1,t2; ① join等待一定是串行的只能t2先执行完再继续执行t1; ② wait和notify, 中的wait只是等待线程中的一部分可以先让t2执行完一部分再执行t1, t1执行一部分t2再继续执行(可以调整执行的顺序)
wait 使用的两种方式 使用wait会阻塞等待让线程进入WAITING状态 ① 带参数 的wait(时间带参数的指最大等待时间等到最大时间没人通知就会自己唤醒自己 ② 不带参数 的wait : 会一直等待只能等待被唤醒或者被中断
1.3 notify 也需要加锁
前提必须先执行wait, 然后才能有notify; (不然没锁怎么解锁) 虽然没有唤醒wait但是不会让代码出现异常
notifyAll : 唤醒多个线程 【注】如果有多个线程调用waitnotify只能随机唤醒一个而notifyAll 可以把线程全部唤醒此时多个线程重新竞争锁再依次执行
1.4 wait 与 sleep 的区别
① wait用于线程通信sleep用于阻塞线程 ② 设计的初心不同wait解决线程之间顺序控制sleep只能让当前线程休眠一会 ③ wait 是Object的方法sleep是Thread的静态方法 ④ wait使用需要配合synchronized使用sleep不需要;
2.单例模式
什么是单例模式 单例模式是一种经典的设计模式类似于玩游戏的攻略 / 下棋的棋谱 有一些固定的招式
2.1 单例模式的介绍
单例指单个实例一个程序中某个类只能创建出一个实例(对象)不能创建多个对象
java中的单例模式借助java语法保证某个类只能创建出一个实例而不能new多次
2.2 java语法中如何实现单例模式
这里介绍两种方式 ① 饿汉模式 从容的状态比如吃饭洗碗吃完饭马上洗碗 ② 懒汉模式 急迫的状态比如吃饭洗碗吃完饭先不着急洗碗等下一次用的再洗碗
例如计算机读取硬盘中的文件内容并显示 饿汉把文件所有内容全部读到内存中并显示 懒汉只读取文件中的一部分能填充当前屏幕如果翻页再读取其他文件内容如果不翻页就不用再读
2.3 实现单例模式
此处把Singleton设置为单例的
2.3.1 饿汉模式实现单例
饿汉模式开始直接创建实例不管用不用 【注】 ① 唯一实例本体使用static修饰 ② 获取实例方法提供get方法 ③ 禁止外部再创建实例 使用private修饰无参构造(private修饰必须提供get方法来获取实例)
class Singleton {//唯一实例本体private static Singleton instance new Singleton();//被static修饰该属性是类的属性 》 类对象//JVM 中每个类的类对象只有唯一一份类对象的成员也是唯一一份//private修饰需要get方法,外部才能获取到//获取到实例的方法public static Singleton getInstance() {return instance;}//禁止外部new 实例private Singleton() {}
}测试代码 public static void main(String[] args) {//此时singleton1singleton2调用的是同一个对象Singleton singleton1 Singleton.getInstance();Singleton singleton2 Singleton.getInstance();//为了防止new一个新的对象给无参构造用private修饰//Singleton singleton3 new Singleton();//此时不能new了}2.3.2 懒汉模式实现单例模式
懒汉模式开始不创建实例等到使用的时候才会创建
class SingletonLazy {//不马上创建实例private static SingletonLazy instance null;public static SingletonLazy getInstance() {//等到调用的时候才创建实例if(instance null) {instance new SingletonLazy();}//如果有了实例直接返回也保证了只要一个实例return instance;}private SingletonLazy() {}
}测试 public static void main(String[] args) {SingletonLazy singletonLazy1 SingletonLazy.getInstance();SingletonLazy singletonLazy2 SingletonLazy.getInstance();//比较singletonLazy1 singletonLazy2,相等就是同一个对象System.out.println(singletonLazy1 singletonLazy2);}2.3.3 懒汉模式下产生的线程不安全
通过上述两个方式实现单例模式它们是线程安全的吗 可以通过分析线程不安全产生的原因来判断
饿汉模式获取实例方法只是读操作不会修改变量线程是安全的 懒汉模式多线程下无法保证创建对象的唯一性, 线程是不安全的 懒汉模式下两个线程调用getInstance 问题1 产生原因 t1线程先执行 if 判断准备创建实例但还没有创建此时t2 进行 if 判断进入代码块也准备拆改那就实例这就造成创建多个实例的现象》相当于多个线程修改同一个变量了 if 和 new不是原子的会造成创建多个实例对象创建过多会非常占内存空间导致线程不安全
解决问题1方式 加锁 保证 if 和 new 的原子性 【注】加锁不一定线程安全必须保证锁加对位置
代码实现 public static SingletonLazy getInstance() {//给当前类对象加锁synchronized (SingletonLazy.class) {if(instance null) {instance new SingletonLazy();}}return instance;}问题2 每次调用genInstance都会触发锁竞争让加锁操作变得低效
通过问题可以发现懒汉模式的线程不安全只出现在首次创建对象一定对象创建完毕后续调用getInstance就只是读操作就不会产生线程安全》只给第一次加锁即可
解决问题2 方式使用双重 if 解决 ① 外层判断是否要加锁 ② 内层判断是否创建对象
代码实现
public static SingletonLazy getInstance() {//判断是否要加锁如果有对象就不必加锁此时是线程安全的//外层判断是否要加锁if(instance null) {synchronized (SingletonLazy.class) {//等到调用的时候才创建实例//里层判断是否创建对象if(instance null) {instance new SingletonLazy();}}}//如果有了实例直接返回也保证了只要一个实例return instance;}问题3 多线程调用getInstance 进入操作会遇到new SingletonLazy操作 创建实例会产生指令重排序问题
创建实例一般有三步操作: ① 创建内存 ② 调用构造方法 初始化 ③ 把内存地址赋给引用 原因但是②③顺序不能够确定假设两个线程t1,t2; t1可能先执行③顺序操作此时t2执行 if判断发现instance不为空直接返回而t1没有初始化变量等t2只是拿到了不完整的实例对象直接使用就会造成线程安全 【注】上述指令重排序发生的概率很小但不能忽视
解决问题3 使用volatile 禁止指令重排序 volatile private static SingletonLazy instance null;
2.3.4 小结懒汉模式下产生的线程不安全 解决懒汉模式下的线程不安全方式 ① 加锁把 if 和 new操作变成原子的 ② 双重 if 减少不必要的加锁操作 ③ 使用 volatile 禁止指令重排序保证后续线程能拿到完整对象 总结
✨✨✨各位读友本篇分享到内容如果对你有帮助给个赞鼓励一下吧 感谢每一位一起走到这的伙伴我们可以一起交流进步一起加油吧