一、并发编程基础知识

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处理线程中断是有问题的。
相关推荐
布谷歌7 分钟前
Oops! 更改field的数据类型,影响到rabbitmq消费了...(有关于Java序列化)
java·开发语言·分布式·rabbitmq·java-rabbitmq
PXM的算法星球9 分钟前
java(spring boot)实现向deepseek/GPT等模型的api发送请求/多轮对话(附源码)
java·gpt·microsoft
被程序耽误的胡先生13 分钟前
java中 kafka简单应用
java·开发语言·kafka
F202269748625 分钟前
Spring MVC 对象转换器:初级开发者入门指南
java·spring·mvc
楠枬1 小时前
网页五子棋——对战后端
java·开发语言·spring boot·websocket·spring
YXWik61 小时前
23种设计模式
java·设计模式
不修×蝙蝠1 小时前
Tomcat理论(Ⅰ)
java·服务器·java-ee·tomcat
曲奇是块小饼干_1 小时前
leetcode刷题记录(一百零八)——322. 零钱兑换
java·算法·leetcode·职场和发展
hong_zc1 小时前
SpringBoot 配置文件
java·spring boot·后端