1.程序是如何运行的?(存储器的层次结构-CPU、缓存、内存)






注:
1.L1Cache、L2Cache集成在CPU内部,L3Cache一般集成在主板上。



如果利用空间局部性:
查看下面代码片段,

运行结果:

解释:
两种方式运行时间基本相差了4倍,差异原因在于第二种方式不能利用缓存行带来的速度的便利,所以,我们在访问数组的时候,要尽量按照内存中的布局来访问。




独占状态:
核心1将缓存行加载到核心1的缓存中,这个时候缓存行仅存在于核心1缓存中,这个状态叫做独占状态。

共享状态:
核心1将缓存行加载到核心1、核心2的缓存中,这个时候缓存行既存在于核心1缓存中,也存在于核心2缓存中,这个状态叫做共享状态。

解释:
假设A和B使用了volatile,核心1修改缓存A,造成整个缓存行的数据都失效了,这个时候,核心2的缓存也失效了,需要从主存重新获取缓存行,这个时候造成了一个问题效率比较低。

查看下面代码:


解释:
在NodeArr数组里,添加了2个Node内部类对象,因为它们是相邻的,所以它们会在一个缓存行。下面开启2个线程,分别对数组中的对象去赋值,不断修改缓存行,这样的代码效率比较低下。


解释:
这里利用了空间换时间,通过降低了缓存行的空间利用率,但提升了时间性能。每个Node巧妙设置8个long刚好64个字节占满缓存行,导致每个Node的缓存行互不影响,避免因为共享问题,而带来了效率的提升。
2.使用Thread类和Runnable接口创建线程源码深入讲解






如果自定义设置线程名称:

start()底层调用:
这里start0()是一个native,调用了JVM的C++代码。







解释:
tid : 线程ID
attr: 线程属性
javaStart: 线程启动函数执行入口
javaStart


继续源代码跟踪





注意:
这里Runable加了@FunctionInterface注解,如果一个接口只有一个抽象方法,那么可以在接口上加上@FunctionInterface注解,把它标注为函数式接口,好处在于,函数式接口可以使用lamdba表达式来简化它的创建,加了@FunctionInterface注解会使得代码更清晰。

3.两种创建线程方式分析与对比

大白话:
使用集成Thread方式,访问当前线程可以简化直接this.getName(),而使用实现Runnable()方式,访问当前线程,则必须使用Thread.currentThread()方法访问。
集成Tread类

实现Runnable接口


4.使用Callable接口创建线程的原理和应用场景分析










具体实现:


注意:
这里有一个非常要注意的点,futureTask.get(),如果在生产环境中这么使用,会无限制的等待线程返回数据,如果是个下单的场景,线程一直未执行完成,用户会永远卡在这里。

优化:
调用futureTask.get(long timeout, TimeUnit unit),参数等待时间


注意:如果超过等待时间,线程依旧没有返回接口,这会就会报错TimeoutException。

生产环境,注意trycatch捕获处理下异常。


call方法可以抛出异常,run方法不可以:



所以,call()抛异常是有地方可以感知获取到的,但是run()是没有地方可以感知到线程异常的。
5.多线程中的sleep和join方法详解







解释:join() - 使当前线程等待子线程执行完毕,再继续执行。


bash
在 Java 多线程编程中,`join()` 是一个非常重要的方法,用于控制线程的执行顺序。它的作用是让当前线程等待调用 `join()` 的线程执行完毕后再继续执行。
---
### `join()` 方法的作用
- **等待线程结束**:
- 当一个线程调用另一个线程的 `join()` 方法时,当前线程会进入阻塞状态,直到被调用的线程执行完毕。
- 例如,如果线程 A 调用线程 B 的 `join()` 方法,线程 A 会等待线程 B 执行完成后才继续执行。
- **协调线程执行顺序**:
- `join()` 方法常用于确保某些任务在其他任务完成之后再执行。
---
### `join()` 方法的用法
`join()` 方法有以下几种重载形式:
1. **`join()`**:
- 当前线程会无限期等待,直到被调用的线程执行完毕。
```java
Thread thread = new Thread(() -> {
System.out.println("Thread is running");
});
thread.start();
thread.join(); // 当前线程等待 thread 执行完毕
System.out.println("Thread has finished");
```
2. **`join(long millis)`**:
- 当前线程最多等待 `millis` 毫秒。如果超时,当前线程会继续执行。
```java
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread is running");
});
thread.start();
thread.join(1000); // 当前线程最多等待 1 秒
System.out.println("Main thread continues after timeout");
```
3. **`join(long millis, int nanos)`**:
- 当前线程最多等待 `millis` 毫秒 + `nanos` 纳秒。
---
### `join()` 的工作原理
- `join()` 方法是通过调用 `wait()` 实现的。
- 当线程 A 调用线程 B 的 `join()` 方法时,线程 A 会进入等待状态(WAITING),直到线程 B 执行完毕。
- 线程 B 执行完毕后,JVM 会自动调用 `notifyAll()`,唤醒所有等待线程。
---
### `join()` 的注意事项
1. **中断异常**:
- `join()` 方法会抛出 `InterruptedException`,表示当前线程在等待时被中断。
- 需要根据业务场景处理中断。
```java
try {
thread.join();
} catch (InterruptedException e) {
System.out.println("Main thread was interrupted");
}
```
2. **死锁风险**:
- 如果多个线程相互调用 `join()`,可能会导致死锁。
- 例如,线程 A 等待线程 B,线程 B 又等待线程 A。
3. **超时机制**:
- 如果使用 `join(long millis)`,需要处理超时后的逻辑,避免程序长时间阻塞。
---
### 使用场景
1. **任务依赖**:
- 当一个任务需要等待另一个任务完成时,可以使用 `join()`。
```java
Thread task1 = new Thread(() -> {
System.out.println("Task 1 is running");
});
Thread task2 = new Thread(() -> {
try {
task1.join(); // 等待 task1 完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task 2 is running");
});
task1.start();
task2.start();
```
2. **主线程等待子线程**:
- 主线程需要等待所有子线程完成后再继续执行。
```java
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running");
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join(); // 主线程等待所有子线程完成
}
System.out.println("All threads have finished");
```
---
### 总结
- `join()` 方法用于让一个线程等待另一个线程执行完毕。
- 它通过 `wait()` 和 `notifyAll()` 实现线程间的同步。
- 使用时需要注意中断异常、死锁风险和超时机制。
- 适用于任务依赖和主线程等待子线程完成的场景。
6.什么场景使用线程中断?






注意:sleep():如果任何线程中断了当前线程。抛出InterruptedException异常时,当前线程的中断状态将被清除(中断状态(信号)恢复),所以,需要再次捕获InterruptedException异常,重新中断子线程。
大白话,即如果线程中断时,线程正在执行某些阻塞操作,线程会抛出InterruptedException,在抛出nterruptedException的同时,中断状态会被恢复,所以处理中断异常时,我们需要再调用一次中断操作。
具体见代码:
注意:
//采用volatile方式,在没有sleep()、join()的情况下,是没有问题的;
//如果有使用sleep()、join()的情况下,无法让sleep、join感知到有中断,所以在此情况下,用volatile处理线程中断是有问题的。