

—.缓存层级的性能与容量特点

寄存器虽然速度极快,但空间太小、存储能力有限。为了在速度和存储容量之间取得平衡,CPU 开发者在 CPU 上额外建设了 “缓存” 区域,用于临时存储内存数据,提升数据访问效率。
volatile 修饰的变量,能够保证"内存可见性"

代码在写入volatile修饰的变量的时候,
• 改变线程工作内存中volatile变量副本的值
• 将改变后的副本的值从⼯作内存刷新到主内存 代码在读取volatile修饰的变量的时候,
• 从主内存中读取volatile变量的最新值到线程的⼯作内存中
• 从工作内存中读取volatile变量的副本 前面我们讨论内存可见性时说了,直接访问工作内存(实际是CPU的寄存器或者CPU的缓存),速度非常快,但是可能出现数据不⼀致的情况. 加上volatile,强制读写内存.速度是慢了,但是数据变的更准确了
代码示例:
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和t2
• t1中包含一个循环,这个循环以flag==0为循环条件
• t2中从键盘读入⼀个整数,并把这个整数赋值给flag
t1 读的是自己工作内存中的内容
当t2对flag变量进行修改,此时t1感知不到flag的变化
如果给flag加上volatile
static class Counter {
public volatile int flag = 0;
}
// 执⾏效果
// 当⽤⼾输⼊⾮0值时, t1 线程循环能够⽴即结束代码示例:
static class Counter {
volatile public int count = 0;
void increase() {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
final Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
counter.increase();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
counter.increase();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}这个是最初的演示线程安全的代码
给increase方法去掉synchronized
给count加上volatile关键字
此时可以看到,最终count的值仍然无法保证是100000
4个导致死锁的原因
指资源在同一时间只能被一个线程占用。例子:打印机是典型的互斥资源,某时刻只有一个线程(或用户)能使用它打印文档,其他线程必须等待。
指线程占用的资源在未主动释放前,不能被其他线程强行夺取。例子:线程 A 获取了数据库连接资源,在它未完成操作并主动释放连接前,线程 B 无法强制从 A 手中夺取这个连接,只能等待 A 释放。
指线程在保持已有资源的同时,又去请求新的资源。例子:线程 A 已占用资源 1,还在请求资源 2;同时线程 B 已占用资源 2,还在请求资源 1。双方都 “抱着已有资源不放,又想要对方的资源”,就可能引发死锁。
指多个线程之间形成资源请求的循环链。例子:线程 A 等待线程 B 释放资源 2,线程 B 等待线程 C 释放资源 3,线程 C 等待线程 A 释放资源 1,形成 “A→B→C→A” 的循环请求链,导致所有线程都无法继续执行。
由于线程之间是抢占式执行的,因此线程之间执行的先后顺序难以预知
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序
join的 “等待”wait的 “等待”notify/notifyAll,当前线程就会被唤醒继续执行(不需要等对方线程完全执行完)。注意:当多个线程竞争一把锁的时候,获取到锁的线程如果释放了,其他的那个线程拿到的锁?
这是不确定的(随即调度)
操作系统的调度都是随机的,其他的线程都属于在锁上阻塞等待,是阻塞状态。当前的这个释放锁的线程,是就绪的状态。这个线程有很大的概率再次拿到这个锁~~
wait和notify的归属wait和notify是 **Object类的方法 **,这意味着 Java 中任意对象都具备这两个方法,是多线程通信的基础工具。
InterruptedException的意义wait、sleep、join等)都会声明抛出InterruptedException。interrupt方法随时唤醒,用于灵活控制线程的阻塞状态。线程 A 调用 threadB.join() 阻塞等待线程 B 执行完毕,此时线程 C 可以调用 threadA.interrupt(),线程 A 会立即被唤醒,不再等待线程 B 执行完成,同时会抛出 InterruptedException 来提示这种 “被中断唤醒” 的情况。
执行 object.wait() 时,第一件事就是释放 object 对象对应的锁。但要执行这个释放操作,前提是 object 对象当前处于加锁状态(即当前线程已经获取了 object 的锁)。

wait()/wait(long timeout):让线程进入等待状态wait():无限期等待,必须通过 notify()/notifyAll() 唤醒。wait(long timeout):等待指定毫秒数后自动唤醒;若在超时前被唤醒,也会提前返回。notify()/notifyAll():唤醒等待的线程notify():随机唤醒一个处于该对象等待队列中的线程。notifyAll():唤醒所有处于该对象等待队列中的线程。wait() 之后的代码。使当前执行代码的线程进行等待(把线程放到等待队列中)
释放当前的锁
满足一定条件时被唤醒,重新尝试获取这个锁
wait 要搭配synchronized来使用,脱离synchronized使用wait会直接抛出异常
wait 结束等待的条件:
其他线程调用该对象的notify方法
wait等待时间超时(wait方法提供一个带有timeout参数的版本,来指定等待时间)
其他线程调用该等待线程的interrupted方法,导致wait抛出 InterruptedException 异常
代码示例:观察wait()方法使用
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized (object) {
System.out.println("等待中");
object.wait();
System.out.println("等待结束");
}
}这样在执行到object.wait()之后就一直等待下去,那么程序肯定不能一直这么等待下去了。这个时候就需要使用到了另外一个方法唤醒的方法notify()。
2、notify()方法 notify 方法是唤醒等待的线程
方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
如果有多个线程等待,则有线程调度器随机挑选出一个呈wait状态的线程。(并没有"先来后到")
在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()⽅法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁。
代码示例:使用notify()方法唤醒线程
创建WaitTask类,对应一个线程,run内部循环调用wait
创建NotifyTask类,对应另一个线程,在run内部调用一次notify
注意,WaitTask和NotifyTask内部持有同一个Objectlocker.WaitTask 和 NotifyTask要想配合就 需要搭配同一个Object
notify方法只是唤醒某⼀个等待线程,使用notifyAll方法可以一次唤醒所有的等待线程
范例:使用notifyAll()方法唤醒所有等待线程
当然为了面试的目的,我们还是总结下:
1)wait需要搭配synchronized使用.sleep不需要
2)wait是Object的方法sleep是Thread的静态方法

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();
}