文章目录
前言
这篇博客博主开始讲述多线程的下部,上部在博主的上一篇博客,需要的柚柚们可以看看~(链接在这里点击即可)。
提示:以下是本篇文章正文内容
一、volatile关键字
volatile 能保证内存可见性
代码在写入 volatile 修饰的变量的时候
- 改变线程工作内存中volatile变量副本的值
- 将改变后的副本的值从工作内存刷新到主内存
代码在读取 volatile 修饰的变量的时候- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取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) {
}
System.out.println("循环结束!");
});
Thread t2 = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("输⼊⼀个整数:");
counter.flag = scanner.nextInt();
});
t1.start();
t2.start();
}
- t1读的是自己的工作内存中的内容
- 当 t2 对 flag 变量进行修改, 此时 t1 感知不到 flag 的变化.
static class Counter {
public volatile int flag = 0;
}
- 此时当用户输入非零值时,t2将flag的值刷回内存,然后t1再从内存中重新读取flag的值,我们可以发现t1线程循环能够立即结束。
二、wait 和 notify
由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.
完成这个协调工作, 主要涉及到三个方法:
- wait() / wait(long timeout): 让当前线程进入等待状态
- notify() / notifyAll(): 唤醒在当前对象上等待的线程
注:
wait, notify, notifyAll 都是 Object 类的方法.
2.1 wait()方法
wait 做的事情:
- 使当前执行代码的线程进行等待. (把线程放到等待队列中)
- 释放当前的锁
- 满足⼀定条件时被唤醒, 重新尝试获取这个锁
注:wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.
wait 结束等待的条件:
- 其他线程调用该对象的 notify/notifyAll 方法.
- wait 等待时间超时 (wait 方法提供⼀个带有 timeout 参数的版本, 来指定等待时间).
- 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常
代码示例:
public static void main(String[] args) throws InterruptedException {
Object object =