当前位置: 首页 > news >正文

Java线程安全:volatile与wait/notify详解

目录

一. volatile

1.1内存可见性

1.2 volatile的使用

二. wait & notify

2.1 wait()

2.2 notify()

2.3 notifyAll()

2.4 具体流程


一. volatile

volatile 是Java中的一个关键字,主要是解决线程安全问题中内存可见性的问题

在了解volatile之前我们要先了解一下什么是内存可见性

1.1内存可见性

内存可见性是 多线程环境下,一个线程修改共享变量后,其他线程能否“立刻看到”这个修改结果 的特性。

下面来看一个经典的例子:

public class Text {public static int flog=0;public static void main(String[] args) {Thread t1=new Thread(()->{Scanner sc=new Scanner(System.in);flog=sc.nextInt();});Thread t2=new Thread(()->{while (flog==0){//什么也不做}});t1.start();t2.start();}
}

     在这个代码中就会出现由于内存可见性而导致输入1后仍然不能退出循环

原因:

站在CPU指令的角度:

1.load操作会从内存中读取flog的值,到寄存器中

2.cmp操作会将flog与寄存器中的值进行比较并且判断跳转

虽然在另一个线程中有flog值的修改,但是编译器无法分析出另一个线程的执行时机,并且load操作的开销远远大于cmp的开销,所以编译器做出了一个大胆的判定:将load操作进行优化,优化为复用寄存器/缓存中的旧值

所以当我们输入1时,而flog读取的仍然是存放在寄存器/缓存中的旧值0,而导致一直陷入循环,不能退出

1.2 volatile的使用

volatile是Java中的关键字,直接修饰可能会触发内存可见性问题的变量后即可

public class Text {public  volatile static int flog=0;public static void main(String[] args) {Thread t1=new Thread(()->{Scanner sc=new Scanner(System.in);flog=sc.nextInt();});Thread t2=new Thread(()->{while (flog==0){}});t1.start();t2.start();}
}

注意:

  • volatile只能解决内存可见性导致的线程安全问题,并不能保证原子性
  • volatile只能应对一个线程读一个线程写的操作,不能应对两个线程写主要是原子性的问题,而锁不仅能保证原子性还能顺便解决内存可见性问题)

问题:若是在while中加入sleep()能阻止内存可见性问题吗?

答:能  ,因为内存可见性问题本质上是由编译器优化带来的,而由于sleep的引入会抑制编译器对load的优化,从而解决了内存可见性问题(不是sleep影响内存可见性,而是影响编译器的优化)

我们还要知道的一点:在循环体中的各种复杂操作,都可能会引其上述的优化失效

public class Text {public   static int flog=0;public static void main(String[] args) {Thread t1=new Thread(()->{Scanner sc=new Scanner(System.in);flog=sc.nextInt();});Thread t2=new Thread(()->{while (flog==0){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

加入sleep后就不存在线程安全问题了

二. wait & notify

由于线程之间的执行是抢占式的,我们难以预料线程执行的先后顺序。但是在实际开发中我们希望合理的协调多线程之间的执行顺序。就比如假期出行,我们要先订票支付后,平台才会给我们安排座位位置。

在Java中就涉及三个方法能够帮助我们完成整个流程

2.1 wait()

wait()要做的三件事

  1. 执行到wait后释放当前线程的锁
  2. 等待其他线程的通知
  3. 当通知到达后,线程会先从等待状态进入阻塞状态(去竞争锁),等成功获取锁后,才会进入就绪状态

上述的1和2是原子的

wait()被唤醒的条件

  1. 执行到notify(),被notify唤醒
  2. wait等待超时【wait(long timeout)/wait(long timeout,int nanos)】---设置超时时间
  3. 当其它线程调用该线程的 interrupt 方法,wait 会抛出异常使其退出等待状态进入阻塞状态

注意:

  • wait必须要在synchronized中使用
  • wait()是Object类 的方法

2.2 notify()

notify是唤醒等待的线程

注意:

  • notify唤醒的是当前对象锁上,处于wait 等待状态的其中一个线程(随机唤醒其中一个)
  • notify必须要在synchronized中使用
  • notify()是Object类 的方法

2.3 notifyAll()

与notify()不同的是它唤醒处于wait 等待状态的所有线程,其余与notify一致

2.4 具体流程

注意:

  • synchronized  锁定的对象、调用 wait()  方法的对象、调用 notify() / notifyAll()  方法的对象,必须是同一个对象。要配套使用,否则会通知无效
  • wait要保证在notify之前执行,不然执行到wait时会出现死等状态(没有notify再唤醒它)

2.5 wait 与 sleep 的区别

  1. wait 要释放锁,而sleep 只是让线程陷入休眠,不会释放锁
  2. wait 必须要搭配锁使用,而 sleep 不需要
  3. 虽然 wait 和 sleep 都能被 interrupt 唤醒,但是 wait 设计的初衷是更希望被notify唤醒

http://www.dtcms.com/a/412812.html

相关文章:

  • 做网站 pc端与手机端兼容淘宝店可以做团购的网站吗
  • php源码怎么建设网站响应网站怎么做
  • 长春网站设计哪家好网站是com好点还是cn
  • 住建部官方网站许昌市住房和城乡建设部网站
  • C++ 继承与派生详解:从概念到代码实战
  • C++扩展 --- 并发支持库(补充3)
  • 高级建站网站十大装饰公司排行榜
  • 苏州专门网站网络架构有几种模式
  • 深度学习任务
  • 网站建设思路及设计方案wordpress如何汉化
  • 营销网站建设设计菏泽 网站建设公司
  • 做素材类的网站赚钱吗青岛胶南做网站的有多少
  • 如何查询网站是否有做404商业模式包括哪些模式
  • 网站被降权表现佛山网站专家
  • 重庆做网站制作的公司做网站去哪找客户
  • 土地 水利 勘测设计 公司宣传册设计样本江门当地的免费网站优化
  • “常小豚苏超限定款”公益联名:以热爱之名守护生态赛场
  • 深圳网站建设设计首选公司搜索关键词排名推广
  • 怎么把自己网站推广出去在线域名注册
  • 贵州网站备案查询ftp跟网络连接Wordpress
  • 南京手机网站制作网络营销成功案例3篇
  • wordpress 多站点主题精准信息预测
  • 高端网站自己怎么做网页
  • 【香橙派开发笔记】中文界面与输入法配置
  • 做网站虚拟主机哪家好房地产网站建设招商
  • 大气的企业网站源码新西兰网站开发专业
  • 上海模板网站建站易网网站
  • 二手书网站建设的目的建设银行官方网站地址
  • Confluence API 常用操作的 Python 示例集合
  • 哪些域名不能够做淘宝客网站做内贸哪个网站找客户