本文介绍: 一、volatile 关键字一、volatile 关键字1、volatile 能保证内存可见性我们前面的线程安全文章中,分析引起线程不安全的原因,其中就有一个原因是因此,我们就引入了volatile 关键字,volatile 修饰的变量,能够保证 “内存可见性”。(这里的“工作内存”不是真正的内存,就像CPU寄存器。
一、volatile 关键字
1、volatile 能保证内存可见性
- 创建两个线程 t1 和 t2
- t1 中包含⼀个循环, 这个循环以 flag == 0 为循环条件.
- t2 中从键盘读入⼀个整数, 并把这个整数赋值给 flag
- 预期当用户输入非 0 的值的时候, t1 线程结束.
static class Counter {
public int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
while (counter.flag == 0) {
// do nothing
}
System.out.println("循环结束!");
});
Thread t2 = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("输⼊⼀个整数:");
counter.flag = scanner.nextInt();
});
t1.start();
t2.start();
}
// 执⾏效果
// 当用户输⼊⾮0值时, t1 线程循环不会结束. (这显然是⼀个 bug)
这里t1线程循环并不会结束,这是因为 t1 读的是自己工作内存中的内容,当 t2 对 flag 变量进行修改,此时 t1 感知不到 flag 的变化。
static class Counter {
public volatile int flag = 0;
}
// 执⾏效果
// 当用户输⼊⾮0值时, t1 线程循环能够⽴即结束.
2、volatile 不保证原子性
public class ThreadDemo {
private static volatile long count = 0;
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(()->{
for (int i = 1;i <= 500000;i++) {
count++;
}
});
Thread t2 = new Thread(()->{
for (long i = 0;i < 500000;i++) {
count++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("count= "+count);
}
}
此时,最终 count 的值仍然无法保证是 1000000。所以volatile 不保证原子性,volatile 保证的是内存可见性。
二、wait 和 notify
注意:wait, notify, notifyAll 都是 Object 类的方法
1、wait()方法
wait 做的事情:
这里要注意,wait 要搭配 synchronized 来使用,脱离 synchronized 使用 wait 会直接抛出异常。
- 其他线程调用该对象的 notify 方法
- wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本,来指定等待时间)
- 其他线程调用该等待线程的 interrupted 方法,导致 wait 抛出 InterruptedException 异常
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object) {
System.out.println("等待中");
object.wait();
System.out.println("等待结束");
}
}
2、notify()方法
notify 方法是唤醒等待的线程的:
- 方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
- 如果有多个线程等待,则由线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 “先来后到”)
- 在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁
- 创建 WaitTask 类, 对应⼀个线程, run 内部循环调用wait.
- 创建 NotifyTask 类, 对应另⼀个线程, 在 run 内部调用一次 notify
- 注意, WaitTask 和 NotifyTask 内部持有同⼀个 Object locker,WaitTask 和 NotifyTask 要想配合就需要搭配同一个 Object。
public class ThreadDemo {
static class WaitTask implements Runnable {
private Object locker;
public WaitTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
synchronized (locker) {
while (true) {
try {
System.out.println("wait 开始");
locker.wait();
System.out.println("wait 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
static class NotifyTask implements Runnable {
private Object locker;
public NotifyTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
synchronized (locker) {
System.out.println("notify 开始");
locker.notify();
System.out.println("notify 结束");
}
}
}
public static void main(String[] args) throws InterruptedException {
Object locker = new Object();
Thread t1 = new Thread(new WaitTask(locker));
Thread t2 = new Thread(new NotifyTask(locker));
t1.start();
Thread.sleep(1000);
t2.start();
}
}
3、notifyAll()方法
static class WaitTask implements Runnable {
// 代码不变
}
static class NotifyTask implements Runnable {
// 代码不变
}
public static void main(String[] args) throws InterruptedException {
Object locker = new Object();
Thread t1 = new Thread(new WaitTask(locker));
Thread t3 = new Thread(new WaitTask(locker));
Thread t4 = new Thread(new WaitTask(locker));
Thread t2 = new Thread(new NotifyTask(locker));
t1.start();
t3.start();
t4.start();
Thread.sleep(1000);
t2.start();
}
此时可以看到,调用notify 只能唤醒⼀个线程 。
public void run() {
synchronized (locker) {
System.out.println("notify 开始");
locker.notifyAll();
System.out.println("notify 结束");
}
}
此时可以看到,调用 notifyAll 能同时唤醒 3 个wait 中的线程。
理解 notify 和 notifyAll:notifyAll ⼀下全都唤醒,需要这些线程重新竞争锁
4、wait 和 sleep 的对比
原文地址:https://blog.csdn.net/m0_61876562/article/details/134701735
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_38464.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。