概述
目录
- 前言
- 一、volatile关键字
- 二、wait和notify
- 13. 【面试题】 ==wait和sleep的对比==:
- 三、代码参考
- THINK
前言
今天不学习,明天变垃圾
本文主要是volatile(保证内存空间性)、wait和notify的介绍。
一、volatile关键字
- volatile内存可见性(内存可见性也是线程安全的一个问题)
- CPU读写数据最快,内存次之,硬盘最慢:CPU比内存快3-4个数量级,内存又比硬盘快3-4个数量级
- 编译器的优化在多线程下可能存在误判,如:一个线程只读,一个线程又进行修改
- 解决方法:volatile关键字是内存可见。 volatile是“可变的”,即:可以使用这个关键字来修饰一个变量,此时被修饰的变量 编译器就不会做出“不读内存,只读寄存器”这样的优化;加了该关键字之后,每次代码都会老老实实的读取该变量的值。
-
编译器啥时候优化,啥时候不优化,这个是和写的代码和运行环境密切相关的。
-
synchronized(加锁)保证了线程的可重入性和原子性!! 而volatile是保证“内存可见性”的,但是不保证原子性。
-
针对一个线程读,一个线程修改 的场景,使用volatile是合适的; 针对两个线程修改的场景,volatile是无能为力的,因为没有原子性
(volatile可以多加,但是千万不要少加) -
在面试中一旦谈到了volatile,多半是脱离不了JMM(Java Memory Model:java内存模型)。
① volatile是会禁止编译器优化的,避免直接读取了CPU寄存器中缓存的数据,而是每次都重新读取内存。
② 站在JMM的角度来看待volatile:
正常程序执行过程中会把主内存(也就是所说的内存)的数据先加载到工作内存(也就是所说的CPU寄存器,不是真的内存)中,然后再进行计算处理; 编译器优化可能会导致不是每次都真的读取主内存,而是直接取从工作内存中缓存的数据,这就可能会导致内存可见性问题; volatile起到的效果 就是保证每次读取内存都是真的从主内存中重新读取 -
缓存的存储空间比寄存器大,比内存小; 但是缓存的访问速度比寄存器慢,比内存快。
有时候重复读内存,就不必真的每次都读内存了,而是可以只有第一次读内存,后续就读缓存中的数据就行,这样可以提高效率。(类似于“编译器优化”)
简记法:寄存器 缓存 内存
(寄存器里存的是正在使用的中间结果)
- 【如果面试中问到volatile,最好两个方面都回答。 并且面试最好“举一反三”】
二、wait和notify
- wait和notify用来调配线程线程执行的顺序
- wait是Object方法,而Object是java所有类的祖宗,因此可以使用任意类的实例来调用wait方法; wait可能会抛出InterruptedException异常,这个异常就是被interrupt方法唤醒的。
- 当线程执行到wait的时候就会发生阻塞,直到另一个线程调用notify才会把这个wait唤醒,然后继续往下走。
- IlegalMonitorStateException非法的锁状态异常:此时的Monitor指的其实就是synchronized,在JVM中synchronized也叫做“监视器锁”。
- wait操作 在内部本质上是做三件事:
①释放当前锁;
②进行等待通知;
③满足一定条件的时候(也就是别的线程调用notify)被唤醒,然后重新获取锁
- 所以等待通知的前提是释放锁,而释放锁的前提是先加锁
- wait的第一步操作是释放锁,保证其他线程能够正常往下执行
- wai和加锁操作密不可分。(也就是synchronized之后才可以wait)
wait这里可以记住类似举例:ATM取钱
- notify也是要包含在synchronized里面的。
线程1没有释放锁的话,线程2也就无法调用notify(因为锁在阻塞等待);线程1调用wait,在wait中就释放锁了,这个时候虽然线程1代码阻塞在synchronized里面,但是此时的锁还是在释放状态,线程2 就可以拿到锁。 - 其他线程也是需要上锁才能调用notify,调用了notify就会唤醒wait,wait就会尝试重新加锁,但是wait加锁可能需要阻塞一会儿,直到notify所在的线程释放锁完成后wait才加锁成功。
- 要保证:加锁的对象和调用wait的对象是同一个对象;另外同时还要保证:调用wait的对象和调用notify的对象也是同一对象!!
- 多个线程都在wait时,notify是随机唤醒一个线程; notifyAll则是全都唤醒,但是即使是唤醒了所有的wait,这些wait也是需要重新竞争锁的,而重新竞争锁的过程仍然是串行的,所以这个其实并不是很常用。
- wait, notify, notifyAll 都是 Object 类的方法。
- wait 要搭配 synchronized 来使用, 脱离 synchronized 使用 wait 会直接抛出异常。
- 方法notify()【即:唤醒等待】也要在同步方法或同步块【即:使用synchronized修饰】中调用。
13. 【面试题】 wait和sleep的对比:
答: ① wait 需要搭配 synchronized 使用没有synchronized就会抛异常; sleep 不需要。
② wait 是 Object 的方法, sleep 是 Thread 的静态方法.
其实理论上 wait 和 sleep 完全是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻塞一段时间。
唯一的相同点就是都可以让线程放弃执行一段时间。
(群面进行分组讨论:当个掐表的!还有30s提醒同学们赶紧总结)
三、代码参考
Demo10-13
多线程实例【重要】
THINK
- volatile是保证了内存可见性,但是不保证原子性; synchronized(加锁)保证了原子性和可重入性。
- synchronized、wait、notify是要保证同一对象的!
- wait内部本质上是三步
- wait和notify都要保证在synchronized里面
- 【面试题】sleep和wait的区别
最后
以上就是简单香烟为你收集整理的【javaEE】多线程初阶(Part4 volatile、wait、notify)前言一、volatile关键字二、wait和notify三、代码参考THINK的全部内容,希望文章能够帮你解决【javaEE】多线程初阶(Part4 volatile、wait、notify)前言一、volatile关键字二、wait和notify三、代码参考THINK所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复