<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>NAA&apos;s Blog</title><description>what goes around comes around</description><link>https://natsumeaiovo.github.io</link><item><title>Java并发知识</title><link>https://natsumeaiovo.github.io/blog/260725-java%E5%B9%B6%E5%8F%91/java%E5%B9%B6%E5%8F%91</link><guid isPermaLink="true">https://natsumeaiovo.github.io/blog/260725-java%E5%B9%B6%E5%8F%91/java%E5%B9%B6%E5%8F%91</guid><description>Java 并发知识的学习总结</description><pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Java并发&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190430.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;线程基础知识&lt;/h1&gt;
&lt;h1&gt;1. 什么是 Java 中的线程同步？&lt;/h1&gt;
&lt;h2&gt;回答重点&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mvpnbj5l0i2.feishu.cn/wiki/ItE7wxKfTiQ9Afkc5TFc9cZYn9d?fromScene=spaceOverview#MscxdHKT3olzS9xvHhXc5AGAnec&quot;&gt;&lt;strong&gt;怎么保证线程安全？&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;**是什么：**是一种机制，一种在多线程环境下，&lt;strong&gt;确保同一时刻，只有一个线程可以执行共享资源的临界区代码&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么：&lt;strong&gt;因为&lt;/strong&gt;多个线程&lt;/strong&gt;同时对&lt;strong&gt;共享资源&lt;/strong&gt;（如变量、对象或文件）进行&lt;strong&gt;访问或写入&lt;/strong&gt;的操作，可能会导致&lt;strong&gt;数据不一致、静态条件、死锁&lt;/strong&gt;等问题的产生。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;怎么做：&lt;strong&gt;通过&lt;/strong&gt;加锁&lt;/strong&gt;的方式，来&lt;strong&gt;实现临界区的代码在同一时刻只能被一个线程执行。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;怎么加锁呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;synchronized&lt;/strong&gt;关键字，通过在&lt;strong&gt;方法&lt;/strong&gt;或者&lt;strong&gt;代码块&lt;/strong&gt;上添加synchronized关键字，实现同步机制&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过&lt;strong&gt;ReenTrantLock&lt;/strong&gt;类的 lock() 和 unlock() 方法实现&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过原子类实现，Java中 的 Atomic 类提供了一系列的&lt;strong&gt;原子类&lt;/strong&gt;，例如：&lt;strong&gt;AtomicInteger、AtomicLong、AtomicRefrence&lt;/strong&gt;等，通过这些原子类，可以实现在不使用java代码加锁的形式实现同步机制，原理是这些原子类通过硬件级别的原子操作来实现操作的原子性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;扩展知识&lt;/h2&gt;
&lt;p&gt;共享资源：** **在多线程或者分布式系统中，多个线程、进程、组件都可以执行访问或者写入的操作的资源，常见的共享资源是数据或者对象，比如：实例变量、静态变量、集合等&lt;/p&gt;
&lt;p&gt;静态条件： 当多个线程竞争同一个资源的时候，线程的输出或者行为，取决于线程被CPU调度的时机，而不是程序本身的逻辑&lt;/p&gt;
&lt;p&gt;临界区： 是一段代码，这段代码里写的是，在多线程环境下，对共享资源进行访问或写入的代码逻辑&lt;/p&gt;
&lt;h3&gt;Java 中常见的同步方式&lt;/h3&gt;
&lt;p&gt;https://www.yuque.com/kusanagi-mdqmk/kb/a6a655112e42f32fc335066b2df45513#EJWuq&lt;/p&gt;
&lt;h4&gt;synchronized&lt;/h4&gt;
&lt;p&gt;Java 提供的加锁关键字，用于在方法或代码块上加锁，以确保同一时刻只有一个线程能够执行被同步的方法或代码块。&lt;/p&gt;
&lt;p&gt;synchronized实现同步机制：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;synchronized加在 &lt;strong&gt;实例方法&lt;/strong&gt; 上，锁住的对象是调用方法的&lt;strong&gt;实例对象&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized加在 &lt;strong&gt;静态方法&lt;/strong&gt; 上，锁住的是&lt;strong&gt;类对象&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;synchronized加在 &lt;strong&gt;代码块&lt;/strong&gt; 上，可以&lt;strong&gt;指定一个特定的对象&lt;/strong&gt;作为锁，此方法更加灵活&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;**线程通信：**在 synchronized 可以使用 wait()、notify() 和 notifyAll() 实现条件等待通知。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;wait()：当前线程进入等待状态，直到被其他线程唤醒。必须在同步块或同步方法中调用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;notify()：唤醒一个等待的线程。如果有多个线程在等待，同一时刻只能唤醒一个。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;notifyAll()：唤醒所有等待的线程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如在 synchronized 块或方法中，可以使用 wait() 方法使线程等待某个条件满足，可使用 notify() 或 notifyAll() 方法唤醒等待的线程。&lt;/p&gt;
&lt;h4&gt;ReentrantLock&lt;/h4&gt;
&lt;p&gt;是 JUC （java.util.concurrent）提供的可重入锁，相比 synchronized 它&lt;strong&gt;更加灵活&lt;/strong&gt;，&lt;/p&gt;
&lt;p&gt;ReentrantLock 使用 Condition 对象来提供了更灵活的等待/通知机制。每个 ReentrantLock 可以创建一个或多个 &lt;strong&gt;Condition 对象&lt;/strong&gt;，通过 newCondition() 方法创建。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;await()：使当前线程等待，直到收到信号或被中断。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;signal()：唤醒一个等待线程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;signalAll()：唤醒所有等待线程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相比于 synchronized，ReentrantLock 还提供了&lt;strong&gt;可中断、可超时、公平锁、多条件变量&lt;/strong&gt;等特性。&lt;/p&gt;
&lt;h1&gt;2. 线程和进程？线程状态？&lt;/h1&gt;
&lt;h2&gt;线程、进程 和 协程&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190500.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190503.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;进程是资源分配的最小单位，线程是CPU调度和运行的最小单位&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;线程是进程中的实体，一个进程可以拥有多个线程，一个线程必须有一个父进程。线程不拥有系统资源，只有运行必须的一些数据结构。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程，从而实现程序的并发执行。
一般，线程具有&lt;strong&gt;就绪&lt;/strong&gt;、&lt;strong&gt;阻塞&lt;/strong&gt;和&lt;strong&gt;运行&lt;/strong&gt;三种基本状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在多CPU的系统里，&lt;strong&gt;不同线程可以同时在不同的CPU上运行&lt;/strong&gt;，&lt;strong&gt;甚至当它们属于同一个进程时也是如此&lt;/strong&gt;。大多数支持多处理器的操作系统都提供编程接口来让进程可以控制自己的线程与各处理器之间的关联度（affinity）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线程也称作轻量级进程。就像进程一样，线程在程序中是独立的、并发的执行路径，每个线程有它自己的堆栈、自己的程序计数器和自己的局部变量。但是，与分隔的进程相比，进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其它每个进程应有的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;单核CPU&lt;/strong&gt;在某一时刻其实只能运行一个线程，并不能真正实现并行。但操作系统会通过时间片轮转的调度方式，在多个线程之间频繁切换，让用户感觉好像多个任务“同时”进行（其实是轮流分时执行）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;线程、进程、协程有什么区别？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;进程&lt;/strong&gt;是操作系统中进行&lt;strong&gt;资源分配的最小单位&lt;/strong&gt;，&lt;strong&gt;它拥有自己的独立内存空间和系统资源&lt;/strong&gt;。每个进程都有&lt;strong&gt;独立的堆和栈，不与其他进程共享&lt;/strong&gt;。&lt;strong&gt;进程间通信&lt;/strong&gt;需要通过特定的机制，如&lt;strong&gt;管道、消息队列、信号量&lt;/strong&gt;等。由于进程拥有独立的内存空间，因此其稳定性和安全性相对较高，但同时上下文切换的开销也较大，因为需要保存和恢复整个进程的状态。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程&lt;/strong&gt;是&lt;strong&gt;CPU调度和运行的最小单位&lt;/strong&gt;，线程&lt;strong&gt;共享进程的内存空间，包括堆和全局变量&lt;/strong&gt;。线程之间&lt;strong&gt;通信更加高效&lt;/strong&gt;，因为它们&lt;strong&gt;可以直接读写共享内存&lt;/strong&gt;。线程的&lt;strong&gt;上下文切换开销较小，因为只需要保存和恢复线程的上下文&lt;/strong&gt;，而不是整个进程的状态。然而，由于多个线程共享内存空间，因此存在数据竞争和线程安全的问题，需要通过同步和互斥机制来解决。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;协程&lt;/strong&gt;是一种&lt;strong&gt;用户态的轻量级线程&lt;/strong&gt;，其&lt;strong&gt;调度完全由用户程序控制，而不需要内核的参与&lt;/strong&gt;。协程拥有自己的寄存器上下文和栈，但与其他协程共享堆内存。协程的切换开销非常小，因为只需要保存和恢复协程的上下文，而无需进行内核级的上下文切换。这使得协程在处理大量并发任务时具有非常高的效率。然而，协程需要程序员显式地进行调度和管理，相对于线程和进程来说，其&lt;strong&gt;编程模型更为复杂&lt;/strong&gt;。
Java 在 jdk 19 引入了虚拟线程，算是 java 中的协程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294863446018&quot;&gt;什么是协程？Java 支持协程吗？&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;⭐线程状态&lt;/h2&gt;
&lt;p&gt;在 Java 中，线程的生命周期可以细化为以下几个状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;New（初始状态）：线程对象创建后，但未调用 start() 方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Runnable（可运行状态）：调用 start() 方法后，线程进入就绪状态，等待 CPU 调度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Blocked（阻塞状态）：线程试图获取一个对象锁而被阻塞，或者等待I/O被阻塞。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Waiting（等待状态）：调用wait()，线程进入等待状态，需要被显式唤醒才能继续执行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Timed Waiting（含等待时间的等待状态）：调用sleep()，线程进入等待状态，但指定了等待时间，超时后会被唤醒。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Terminated（终止状态）：线程执行完成或因异常退出。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190545.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;3. 线程创建的方式有哪些？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190549.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190552.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190555.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190559.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190605.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;使用 Runnable 和 Callable 有什么区别？&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190639.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;线程的 run() 方法 和 start() 方法有啥区别？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;run()：  &lt;strong&gt;调用 run() 方法就像调用&lt;/strong&gt;普通方法&lt;/strong&gt;一样，会在当前线程执行任务，不具备多线程效果**。**可以被调用多次&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;start()：只有调用 start() 方法，才会真正&lt;/strong&gt;创建新线程，然后自动调用该线程的 run() 方法，**实现多线程并发。**只能调用一次&lt;/p&gt;
&lt;h1&gt;4. 如何让线程/任务按照顺序执行？&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294959915009&quot;&gt;如何在 Java 中控制多个线程的执行顺序？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;join，线程通信（wait+notify)，ReentrantLock+condition，CountDownLatch，线程池放一个线程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Synchronize + wait/notify&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;public class ThreadOrderControl {
    private static int currentThread = 1; // 当前应执行的线程编号（1, 2, 3）
    private static final Object lock = new Object(); // 共享锁对象

    public static void main(String[] args) {
        Thread t1 = new Thread(() -&gt; runTask(1, &quot;Thread1&quot;));
        Thread t2 = new Thread(() -&gt; runTask(2, &quot;Thread2&quot;));
        Thread t3 = new Thread(() -&gt; runTask(3, &quot;Thread3&quot;));

        t1.start();
        t2.start();
        t3.start();
    }

    private static void runTask(int threadNum, String threadName) {
        synchronized (lock) {
            while (currentThread != threadNum) {
                try {
                    lock.wait(); // 当前线程不该执行，进入等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(threadName + &quot; is running&quot;);
            currentThread++; // 切换到下一个线程
            lock.notifyAll(); // 唤醒所有等待线程
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;join&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-1.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;ReentrantLock&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadOrderControlWithReentrantLock {
    private static int currentThread = 1; // 当前应执行的线程编号（1, 2, 3）
    private static final ReentrantLock lock = new ReentrantLock();
    private static final Condition condition = lock.newCondition();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -&gt; runTask(1, &quot;Thread1&quot;));
        Thread t2 = new Thread(() -&gt; runTask(2, &quot;Thread2&quot;));
        Thread t3 = new Thread(() -&gt; runTask(3, &quot;Thread3&quot;));

        t1.start();
        t2.start();
        t3.start();
    }

    private static void runTask(int threadNum, String threadName) {
        lock.lock(); // 获取锁
        try {
            // 检查是否轮到自己执行
            while (currentThread != threadNum) {
                condition.await(); // 不满足条件，释放锁并等待
            }
            // 执行任务
            System.out.println(threadName + &quot; is running&quot;);
            currentThread++; // 切换到下一个线程
            condition.signalAll(); // 唤醒所有等待的线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); // 必须释放锁
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;notify() 和 notifyAll() 有什么区别？&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-2.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;5. ⭐sleep() 和 wait() 有啥区别？&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;wait和sleep都是放弃cpu使用权，进入等待状态。&lt;/p&gt;
&lt;p&gt;sleep是Thread的静态方法，wait是Object的成员方法。&lt;/p&gt;
&lt;p&gt;sleep通过 Thread.sleep()调用，不会释放锁，&lt;/p&gt;
&lt;p&gt;wait则是需要首先获得锁lock，synchronize(lock)，然后调用lock.wait()来进行等待，调用wait()就会释放该锁 lock。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-3.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;6. 如何停止一个正在运行的线程？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-4.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Volatile + 标记&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里要注意配合 volatile 关键字&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-5.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用thread.stop()方法（不推荐，方法已作废）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用 thread.interrupt()&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;注意&lt;strong&gt;如果打断阻塞（sleep, wait, join）中的线程，线程会抛出 InterruptedException 异常&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用了interrupt()方法后，线程的thread.isInterrupted()会返回 true（默认是false）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-6.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h1&gt;线程安全&lt;/h1&gt;
&lt;h1&gt;7. ⭐说说 synchronized 锁升级？&lt;/h1&gt;
&lt;p&gt;首先synchronized根据不同的使用场景，可能会使用&lt;strong&gt;偏向锁、轻量级锁、重量级锁&lt;/strong&gt;。synchronize使用一个object作为锁，每个对象都有一个&lt;strong&gt;对象头markword，里面就指向了是无锁还是偏向锁、轻量级锁还是重量级锁&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;当&lt;strong&gt;没有锁竞争，就一个线程获取锁&lt;/strong&gt;的时候，是&lt;strong&gt;偏向锁&lt;/strong&gt;，这时会在&lt;strong&gt;对象头markword里记录当前线程的id&lt;/strong&gt;，该线程再次获取该锁，因为记录了id，就可以实现锁重入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**在偏向锁持有期间，另一个线程请求同一把锁，JVM 会撤销偏向锁，并升级为轻量级锁。**锁对象markword记录锁记录的地址，这时线程采用 CAS 自旋获取锁，避免用户态内核态切换导致比较大的消耗。&lt;strong&gt;如果CAS成功，那么这个线程就获得了锁；如果CAS失败，那么就会进入重量级锁阶段。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当轻量级锁阶段的CAS操作失败时，就会升级为重量级锁，底层用一个monitor（owner，waitset，entrylist）。重量级锁意味着线程会进入阻塞状态，等待锁的释放，会导致上下文切换，性能下降。此时没有竞争到锁的线程会&lt;strong&gt;被阻塞，插到一个entrylist里&lt;/strong&gt;，需要等待锁释放来唤醒。线程释放重量级锁时，JVM 会&lt;strong&gt;唤醒所有阻塞的线程，允许它们再次尝试获取锁&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;（什么是自旋锁？）&lt;/p&gt;
&lt;p&gt;自旋锁是在用户态层面实现的，是通过多次自旋去充实竞争锁，避免了用户态到内核态的切换的性能损耗。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-7.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-8.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;上述说的 monitor 是synchronized 的默认实现方式，基于操作系统的互斥量（mutex）实现。每次加锁和解锁都需要进行&lt;strong&gt;内核态和用户态之间的转换&lt;/strong&gt;，导致&lt;strong&gt;性能较低&lt;/strong&gt;。适用于&lt;strong&gt;锁竞争激烈的情况&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;synchronized关键字根据不同的使用场景，可能会使用&lt;strong&gt;偏向锁、轻量级锁、重量级锁&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;Monitor（重量级锁）&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-9.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;notify()&lt;/code&gt; 操作可以让 WaitSet 里面的线程转到 EntryList 中。
也就是说&lt;code&gt;notify()&lt;/code&gt; 只是将等待队列中的一个线程从 &lt;code&gt;WAITING&lt;/code&gt; 状态转为 &lt;code&gt;BLOCKED&lt;/code&gt; 状态。建议在 &lt;code&gt;synchronized&lt;/code&gt; 块的末尾调用 &lt;code&gt;notify()&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那么 &lt;strong&gt;lock 对象 是怎么和 monitor 关联上&lt;/strong&gt;的呢？我们得看看lock对象在 JVM 中的内存结构&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-10.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果 lock 对象关联上了 monitor，那么lock对象头里面的 &lt;strong&gt;MarkWord&lt;/strong&gt; 就会有一个&lt;strong&gt;指向 monitor 的指针&lt;/strong&gt;，从而关联上 monitor。（重量级锁）&lt;/p&gt;
&lt;p&gt;在Java SE 1.6版本之后，&lt;strong&gt;synchronized&lt;/strong&gt;关键字&lt;strong&gt;根据不同的使用场景，可能会使用 偏向锁、轻量级锁或重量级锁&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;轻量级锁&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-11.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-12.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;轻量级锁的加锁和解锁：&lt;/strong&gt;（视频参考：https://www.bilibili.com/video/BV1yT411H7YK?t=503.8&amp;#x26;p=95）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-13.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-14.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;偏向锁（锁重入不用重新 CAS）&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-15.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-16.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;总结，锁升级？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;synchronized&lt;/code&gt;关键字的锁状态（偏向锁、轻量级锁、重量级锁）是&lt;strong&gt;由 JVM 根据线程竞争情况动态调整&lt;/strong&gt;的，这一过程称为&lt;strong&gt;锁升级&lt;/strong&gt;​（或&lt;strong&gt;锁膨胀&lt;/strong&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-17.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;8. 说说 JMM（Java 内存模型）？&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933295031218178&quot;&gt;什么是 Java 内存模型（JMM）？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;首先脑子浮现这个图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-18.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;在多核CPU架构中，每个CPU都有自己的寄存器和多级缓存。&lt;/p&gt;
&lt;p&gt;此时获取数据就有两种方式，第一种方式是直接去操作主内存，但是效率比较低，因为主存和CPU的速度差很多倍。第二种方式就是从缓存中读取，此时性能就非常高，所以CPU肯定是使用第二种方式读取。&lt;/p&gt;
&lt;p&gt;这时候就有问题了，假如有一个共享变量，此时共享变量放在主内存里面的，比如有多线程同时去操作它，而且这两个线程分别在不同的CPU上运行，那么更新缓存的话也都是往自己这一块CPU上的缓存更新(其实CPU和缓存都是焊在一块的)，所以这两个线程对共享遍历变量的操作是不可见的，所以就会线程安全问题。&lt;/p&gt;
&lt;p&gt;而JMM的出现就是为了&lt;strong&gt;解决不同的CPU（线程）之间操作共享变量不可见的问题&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;JMM是啥？&lt;/p&gt;
&lt;p&gt;JMM就是Java Memory Mode也&lt;strong&gt;就是Java内存模型&lt;/strong&gt;。JMM是一种规范，并不是具体实现(注意)。JMM定义了JVM虚拟机和计算机内存(RAM)的工作方式。&lt;strong&gt;把内存分成主内存以及各线程的工作内存，主内存就是计算机内存(RAM)，工作内存就是CPU缓存&lt;/strong&gt;。&lt;strong&gt;规定&lt;/strong&gt;线程操作一个变量的时候&lt;strong&gt;不能直接在主内存里面去改，因为直接在主内存里面去改性能比较低，所以操作共享变量的时候首先得把共享变量复制一份放到工作内存里面，&lt;strong&gt;操作之后还要&lt;/strong&gt;保证必须把这个共享变量刷到主内存里面让其他的CPU线程能够看到共享变量的修改&lt;/strong&gt;。JMM是&lt;strong&gt;解决了可见性、有序性 和 原子性。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JMM解决了什么问题？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第一个：&lt;strong&gt;可见性&lt;/strong&gt;。更改共享变量其他的线程也要看见，使用的就是volatile&lt;/p&gt;
&lt;p&gt;第二个：&lt;strong&gt;有序性&lt;/strong&gt;。保证指令不被重排，volatile&lt;/p&gt;
&lt;p&gt;第三个：&lt;strong&gt;原子性&lt;/strong&gt;。可以使用synchronied解决&lt;/p&gt;
&lt;p&gt;happens-before规则？&lt;/p&gt;
&lt;p&gt;这个就是规定了对共享变量的写操作对其他线程的读操作可见，以及执行顺序的保障。&lt;/p&gt;
&lt;p&gt;如果A线程在B线程执行之前执行，那就是A happens before B，这个时候就需要保证可见性，也就是B能读取到A最新修改的值。然后保证了顺序性，不管编译器怎么样去优化，不管处理器怎么样去重排序，都不能影响A先执行然后再执行B这个原则。&lt;/p&gt;
&lt;p&gt;注意JMM是一种规范，而happens before是规则。讲清除一点就是，JMM是一个规范，而happens-before是遵循这个规范之后表现出来的规则。&lt;/p&gt;
&lt;h1&gt;9. CAS 你知道吗？&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933295027023873?screen=full&quot;&gt;什么是 Java 的 CAS（Compare-And-Swap）操作？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CAS 能够&lt;strong&gt;保证原子性，且是在无锁状态保证原子性&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;假如有线程A和线程B，对一个变量 a = 1并发修改，线程A要进行 + 1，线程B要进行 - 1，我们要保证有序性，那最终结果应该还为1，就可以使用CAS。那线程A和线程B的工作内存都读取到主内存中 a = 1到各自的工作内存，各自执行操作，A线程工作内存中a变为2，B线程变为0，那这时
A线程工作内存的a刷到内存了，内存里的a变为了2，那次是线程B进行CAS操作的时候，就比较了原来内存里的a应该为1，但此时是2，那么就需要重新自旋，拉取新的a的值，a=2，再进行 - 1操作，此时就保证了有序性（可见性、原子性）。&lt;/p&gt;
&lt;p&gt;包括我们synchronize里的轻量级锁，也是CAS操作，比较锁对象头里字段，如果是无锁，就可以进行加锁。如果指针是指向自己当前这个线程，如果是，就可以进行重入锁，或者进行解锁。如果是指向其他线程，那么就需要自旋等待。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;无锁并发：CAS 操作不使用锁，因此&lt;strong&gt;不会导致线程阻塞&lt;/strong&gt;，提高了系统的并发性和性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;原子性：CAS 操作是&lt;strong&gt;原子&lt;/strong&gt;的，保证了&lt;strong&gt;线程安全&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ABA 问题：CAS 操作中，如果一个变量值从 A 变成 B，又变回 A，CAS 无法检测到这种变化，可能导致错误。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自旋开销：CAS 操作通常通过&lt;strong&gt;自旋实现，可能导致 CPU 资源浪费&lt;/strong&gt;，尤其在高并发情况下。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;单变量限制：CAS 操作仅适用于单个变量的更新，不适用于涉及多个变量的复杂操作。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-19.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-20.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-21.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;CAS 底层实现&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-22.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;ABA 问题&lt;/h2&gt;
&lt;p&gt;ABA 问题是指当变量值从 A 变为 B 再变回 A 时，CAS 操作无法检测到这种变化。解决 ABA 问题的一种常见方法是引入版本号或时间戳，每次更新变量时同时更新版本号，从而检测到变量的变化。&lt;/p&gt;
&lt;p&gt;用版本号或者时间戳解决。做任何一个值的修改，都加一个版本号，在进行CAS操作的时候，除了比较内存中的实际值与期望值外，还比较版本号。版本号相同就修改，否则失败重试。&lt;/p&gt;
&lt;p&gt;java里面的AtomicStampedReference，就使用版本号解决了ABA问题，里面是维护了一个int类型的stamp。&lt;/p&gt;
&lt;p&gt;java底层CAS的实现使用的是Unsafe类。X86架构上CAS是使用的cmpxchg和lock指令实现的。AtomicInteger底层依赖于CAS。&lt;/p&gt;
&lt;p&gt;CAS总线风暴：&lt;/p&gt;
&lt;p&gt;lock指令会将缓存通过总线刷到主存，同时通过总线通知其它的cpu核更新自己的缓存，如果cas操作频繁，那么总线上的通信流量就会过大，从而导致总线风暴。&lt;/p&gt;
&lt;h2&gt;乐观锁和悲观锁&lt;/h2&gt;
&lt;p&gt;悲观锁，**先拿锁后干活。**乐观锁，&lt;strong&gt;先干活后检验&lt;/strong&gt;，&lt;strong&gt;检验不对，那就自旋重干&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-23.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;数据库中哪里用到了悲观锁和乐观锁？&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;悲观锁&lt;/strong&gt;通常依赖于数据库自身的锁机制（&lt;strong&gt;行锁、表锁&lt;/strong&gt;），主要由InnoDB存储引擎支持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见使用场景：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;事务中需要&lt;strong&gt;强一致性&lt;/strong&gt;的数据读写&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;银行转账、库存扣减&lt;/strong&gt;等场景&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;典型用法：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;：当前读，对读取的行加&lt;strong&gt;排他锁（写锁），其他事务不能修改&lt;/strong&gt;、不能加FOR UPDATE锁，直到当前事务提交或回滚。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-SQL&quot;&gt;START TRANSACTION;
SELECT stock FROM product WHERE id = 1 FOR UPDATE;
UPDATE product SET stock = stock - 1 WHERE id = 1;
COMMIT;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;上述查询会锁住id=1的那一行（记录锁，可看小林coding），其他事务不能再加FOR UPDATE锁，防止并发扣减库存导致超卖。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;**乐观锁 **需应用层实现，MySQL本身没有内置的乐观锁机制。常用方法：&lt;strong&gt;在表中增加version字段或last_update_time字段&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为啥要乐观锁：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;性能要求高，对一致性要求不是特别严格&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例如：订单状态修改、用户资料更新等&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;乐观锁怎么做：CAS操作&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先读取数据及其版本号&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更新时带上版本号，只在版本号未变时更新成功，否则失败&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-SQL&quot;&gt;-- 假设表结构：-- id | stock | version
-- 1. 查询当前库存和版本号
SELECT stock, version FROM product WHERE id = 1;

-- 2. 更新时，带上版本号UPDATE product
SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 5;
-- 如果受影响行数为1，说明更新成功；为0，则说明有并发，需重试
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;总结对比&lt;/h2&gt;
&lt;h1&gt;10. 说说 volatile 关键字的理解？&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;volatile可以保证线程安全吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;volatile关键字&lt;strong&gt;可以保证可见性，但不能保证原子性&lt;/strong&gt;，因此不能完全保证线程安全。volatile关键字用于修饰变量，当一个线程修改了volatile修饰的变量的值，其他线程能够立即看到最新的值，从而避免了线程之间的数据不一致。&lt;/p&gt;
&lt;p&gt;但是，volatile并&lt;strong&gt;不能解决多线程并发&lt;/strong&gt;下的复合操作问题，比如** i++ 这种操作不是原子操作**，如果**多个线程同时对i进行自增操作，**&lt;strong&gt;volatile不能保证线程安全&lt;/strong&gt;。对于复合操作，**需要使用 synchronized ****、**&lt;strong&gt;Lock 或 CAS 操作来保证原子性和线程安全&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Synchronized：Synchronized是一种排他性的同步机制，保证了多个线程访问共享资源时的互斥性，即同一时刻只允许一个线程访问共享资源。通过对代码块或方法添加 Synchronized 关键字来实现同步。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Volatile：Volatile是一种轻量级的同步机制，用来&lt;strong&gt;保证变量的可见性和禁止指令重排序&lt;/strong&gt;。当一个变量被声明为 Volatile 时，线程在读取该变量时会&lt;strong&gt;直接从内存中读取，而不会使用缓存&lt;/strong&gt;，同时对该变量的&lt;strong&gt;写操作会立即刷回主内存，而不是缓存在本地内存中&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-24.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-25.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;① 保证线程间的可见性&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-26.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-27.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;② 禁止指令重排序&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-28.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-29.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;11. 什么是 AQS？&lt;/h1&gt;
&lt;p&gt;简单来说** AQS** 就是&lt;strong&gt;多线程中的队列同步器&lt;/strong&gt;，它&lt;strong&gt;是****JUC包中的多个组件的底层实现&lt;/strong&gt;。起到了一个抽象、封装的作用，将一些&lt;strong&gt;排队、入队、加锁、中断&lt;/strong&gt;等方法提供出来，便于其他相关 JUC 锁的使用，具体加锁、入队的实现都需要实现类自己控制。&lt;/p&gt;
&lt;p&gt;常见的实现类比如 &lt;strong&gt;ReentrantLock、CountDownlatch、Semaphore&lt;/strong&gt;都是用到了AQS。&lt;/p&gt;
&lt;p&gt;AQS 常见的实现类有 &lt;code&gt;ReentrantLock、CountDownLatch、Semaphore&lt;/code&gt; 等等。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于锁机制：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AQS&lt;/strong&gt;提供了&lt;strong&gt;两种锁&lt;/strong&gt;的机制，分别是&lt;strong&gt;排他锁&lt;/strong&gt;和&lt;strong&gt;共享锁&lt;/strong&gt;。
&lt;strong&gt;独占锁，&lt;strong&gt;多个线程中只能有一个线程去获得该锁的资源。比如lock中的&lt;/strong&gt;ReentrantLock&lt;/strong&gt;，他的一个实现就是用到了中AQS的一个独占锁的功能。
**共享锁，**就是允许多个线程同时获得锁的资源。比如CountDownlatch以及Semaphore都用到了AQS中的共享锁的功能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于state：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;它主要通过维护一个volatile 修饰的&lt;strong&gt;共享状态（state）&lt;/strong&gt;，来管理线程对共享资源的访问，例如互斥锁state为0表示当前没有任何线程竞争到锁资源。而大于等于1表示已经有线程正在持有锁资源。一个线程来获得锁资源，会检查state是否为0，如果是0表示无锁，会通过CAS尝试把state变为1（如果不是0说明有人占了，看看是不是自己，如果是就重入），&lt;strong&gt;AQS采用了CAS机制去保证state复制变量更新的一个原子性&lt;/strong&gt;。
&lt;strong&gt;注意这里，没获得锁的线程不是自旋，而是进行阻塞，加入到一个双向链表的结构中&lt;/strong&gt;**。当获得锁资源的线程在释放锁之后会从这样一个双向链表的头部去唤醒下一个等待的线程，再去竞争锁。**&lt;/p&gt;
&lt;p&gt;状态可以表示多种含义，例如&lt;strong&gt;在 ReentrantLock 中，状态表示锁的重入次数；在 Semaphore 中，状态表示可用的许可数&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于等待队列：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AQS 维护了一个** FIFO 的等待队列**，用于管理等待获取同步状态的线程。每个节点（Node）代表一个等待的线程，节点之间通过 next 和 prev 指针链接。当一个线程获取同步状态失败时，它会被添加到等待队列中，并自旋等待或被阻塞，直到前面的线程释放同步状态。
持有锁的线程释放锁时，会将state修改为0，并对阻塞队列的第一个线程进行唤醒，被唤醒的线程就可以进行锁的竞争了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于公平锁和非公平锁：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最后，&lt;strong&gt;AQS可以实现公平锁和非公平锁&lt;/strong&gt;。
&lt;strong&gt;公平锁就是来了个线程，需要排到阻塞队列里面，排队等待锁。非公平锁就是该线程不管阻塞队列，直接去尝试更改state去竞争锁。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-30.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-31.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;AQS 基本工作机制&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-32.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-33.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;AQS 是&lt;strong&gt;公平锁&lt;/strong&gt;吗？&lt;/h2&gt;
&lt;p&gt;在 AQS 的不同实现类中，&lt;strong&gt;公平锁和非公平锁都能实现&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-34.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;12. 说说 ReentrantLock 的实现原理？&lt;/h1&gt;
&lt;p&gt;ReentrantLock是 JUC 包下的一个&lt;strong&gt;基于 CAS + AQS**** &lt;strong&gt;实现的一种 &lt;strong&gt;可重入 &lt;strong&gt;的&lt;/strong&gt;互斥锁&lt;/strong&gt;&lt;/strong&gt;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;类似于synchronized，是一个&lt;strong&gt;互斥锁&lt;/strong&gt;，可以保证线程安全。相对于synchronize，ReentrantLock具有以下的特点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可中断&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可设置获取锁的超时时间tryLock&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可设置为公平锁&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;支持设置多个条件变量&lt;/strong&gt;（不同线程可以等待在不同的条件变量上，然后唤醒特定条件变量上的线程）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;public class ReentrantLockDemo {

    public static void main(String[] args) throws InterruptedException {
        // 1. 可中断演示
        System.out.println(&quot;--- 1. 可中断演示 ---&quot;);
        final Lock lock1 = new ReentrantLock();
        Thread t1 = new Thread(() -&gt; {
            lock1.lock();
            System.out.println(&quot;T1: 持有锁，不释放&quot;);
            try { Thread.sleep(5000); } catch (InterruptedException e) {}
        });
        Thread t2 = new Thread(() -&gt; {
            System.out.println(&quot;T2: 尝试获取锁 (可中断)&quot;);
            try {
                lock1.lockInterruptibly();
                System.out.println(&quot;T2: 获取到锁&quot;);
            } catch (InterruptedException e) {
                System.out.println(&quot;T2: 等待时被中断!&quot;);
            }
        });
        t1.start();
        Thread.sleep(100);
        t2.start();
        Thread.sleep(500);
        t2.interrupt(); // 中断 t2
        t1.join(); // 等待演示结束

        // 2. 超时演示
        System.out.println(&quot;\n--- 2. 超时演示 ---&quot;);
        final Lock lock2 = new ReentrantLock();
        Thread t3 = new Thread(() -&gt; {
            lock2.lock();
            System.out.println(&quot;T3: 持有锁&quot;);
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            lock2.unlock();
            System.out.println(&quot;T3: 释放锁&quot;);
        });
        Thread t4 = new Thread(() -&gt; {
            System.out.println(&quot;T4: 尝试获取锁 (超时1秒)&quot;);
            try {
                if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println(&quot;T4: 获取到锁&quot;);
                    lock2.unlock();
                } else {
                    System.out.println(&quot;T4: 1秒后超时，未获取到锁&quot;);
                }
            } catch (InterruptedException e) {}
        });
        t3.start();
        Thread.sleep(100);
        t4.start();
        t3.join();

        // 3. 公平锁演示
        System.out.println(&quot;\n--- 3. 公平锁演示 ---&quot;);
        final Lock fairLock = new ReentrantLock(true); // true = 公平
        Runnable fairTask = () -&gt; {
            System.out.println(Thread.currentThread().getName() + &quot; 请求锁&quot;);
            fairLock.lock();
            System.out.println(&quot;&gt;&gt; &quot; + Thread.currentThread().getName() + &quot; 获取锁&quot;);
            fairLock.unlock();
        };
        for (int i = 0; i &amp;#x3C; 3; i++) new Thread(fairTask, &quot;Fair-&quot; + i).start();
        Thread.sleep(1000); // 等待演示结束

        // 4. 条件变量演示
        System.out.println(&quot;\n--- 4. 条件变量演示 ---&quot;);
        final Lock lock3 = new ReentrantLock();
        final Condition condition = lock3.newCondition();
        Thread t5 = new Thread(() -&gt; {
            lock3.lock();
            try {
                System.out.println(&quot;T5: 进入等待&quot;);
                condition.await();
                System.out.println(&quot;T5: 被唤醒&quot;);
            } catch (InterruptedException e) {} 
            finally { lock3.unlock(); }
        });
        t5.start();
        Thread.sleep(500);
        lock3.lock();
        System.out.println(&quot;Main: 准备唤醒 T5&quot;);
        condition.signal();
        lock3.unlock();
        t5.join();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-35.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;实现原理&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-36.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;ReentrantLock的多条件变量有什么用？使用场景呢？&lt;/h2&gt;
&lt;p&gt;作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;精准控制线程等待/唤醒：&lt;strong&gt;每个条件变量都可以维护一组等待线程&lt;/strong&gt;，&lt;code&gt;await()&lt;/code&gt; 让线程在对应的条件下等待，&lt;code&gt;signal()&lt;/code&gt; 或 &lt;code&gt;signalAll()&lt;/code&gt; 唤醒特定条件下的线程。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;比 synchronized/notify/notifyAll 更灵活：传统的 &lt;code&gt;wait/notify&lt;/code&gt; 只能唤醒全部等待的线程，无法区分不同等待条件的线程。而多条件变量能&lt;strong&gt;细分等待队列，实现更细粒度的线程协作&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比如有一个容量固定的队列，有一些生产者进程和消费者进程，我们可以创建两个条件变量notFull、notEmtpy，队列满的时候，生产者会等待在notFull条件变量上，等待消费者线程进行唤醒；同样地，队列空的时候，消费者要消费则会等待在notEmpty条件变量上，等待生产者线程进行唤醒。这样就避免了一下唤醒所有的线程。&lt;/p&gt;
&lt;h1&gt;13. Synchronized 和 Lock 有啥区别？&lt;/h1&gt;
&lt;p&gt;（&lt;strong&gt;语法&lt;/strong&gt;）synchronize是 java 提供的同步关键字，而 Lock 是 JUC 包里提供的一个接口，其中有很多实现类，例如ReentrantLock这样的可重入锁的实现类。
（&lt;strong&gt;锁的粒度&lt;/strong&gt;）synchronize可以加在方法或者代码块上控制锁的粒度，而 lock 则更加灵活，可以通过lock.lock()和lock.unlock()方法来控制锁的粒度，可以自主决定什么时候加锁和释放锁，synchronize锁的释放是被动的，代码块执行结束或出现异常才会释放锁。
&lt;strong&gt;（底层实现）&lt;strong&gt;synchronize底层有一个&lt;/strong&gt;锁升级&lt;/strong&gt;的过程，ReentrantLock则是用了&lt;strong&gt;AQS + CAS&lt;/strong&gt;操作来上锁的
（&lt;strong&gt;lock的特性&lt;/strong&gt;）lock还可以实现&lt;strong&gt;可超时、可打断、公平锁、多条件变量&lt;/strong&gt;等功能。
（&lt;strong&gt;性能&lt;/strong&gt;）性能差别不大，synchronize引入了偏向锁、轻量级锁、重量级锁 和 锁升级的机制来优化。lock 则是通过CAS自旋锁 + AQS来优化。&lt;/p&gt;
&lt;p&gt;Synchronized 修饰静态方法：锁的是这个类的 &lt;code&gt;Class&lt;/code&gt; 对象。也就是说，无论创建了多少个该类的实例，所有的实例共享同一个锁，因为这个锁属于类本身而不是某个对象实例。&lt;/p&gt;
&lt;p&gt;Synchronized 修饰实例方法：锁的是当前实例（调用该方法的对象），也就是这个对象的内在锁。这也就是说每个对象实例都有自己独立的锁。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-37.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;wait/notify 和 await/signal 是怎么使用的？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;**wait/notify：**属于 &lt;strong&gt;Object 类&lt;/strong&gt;的方法，&lt;strong&gt;必须在 &lt;strong&gt;&lt;strong&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;同步块或同步方法中使用&lt;/strong&gt;。锁机制是基于对象的内置监视器锁（&lt;strong&gt;monitor&lt;/strong&gt;），&lt;strong&gt;每个锁对象只有一个等待队列&lt;/strong&gt;，功能相对简单，只支持基本的等待和唤醒。&lt;code&gt;notifyAll()&lt;/code&gt; 会唤醒&lt;/strong&gt;所有在该锁下等待&lt;/strong&gt;的线程。lock.wait(), lock.notify()&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;await/signal：&lt;strong&gt;属于&lt;/strong&gt; Condition 接口&lt;/strong&gt;的方法，&lt;strong&gt;必须在 &lt;strong&gt;&lt;strong&gt;&lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/strong&gt;&lt;/strong&gt; 的 &lt;strong&gt;&lt;strong&gt;&lt;code&gt;lock / unlock&lt;/code&gt;&lt;/strong&gt;&lt;/strong&gt; 之间使用&lt;/strong&gt;。是基于显式锁（ReentrantLock），需要手动创建 Condition 对象。一个 Lock 可以创建多个 Condition，支持多个等待队列，可以精确控制&lt;strong&gt;唤醒特定条件下等待&lt;/strong&gt;的线程，且支持&lt;strong&gt;超时等待&lt;/strong&gt;、&lt;strong&gt;不响应中断的等待&lt;/strong&gt;等高级功能 condition.await(), condition.signal()&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;代码参考&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

/**
 * wait/notify vs await/signal 对比演示
 */
public class ComparisonExample {
    
    /**
     * wait/notify 机制的特点
     */
    public static class WaitNotifyDemo {
        private final Object monitor = new Object();
        private boolean condition = false;
        
        public void waitMethod() {
            synchronized (monitor) {
                try {
                    while (!condition) {
                        System.out.println(&quot;wait/notify: 线程等待中...&quot;);
                        monitor.wait(); // 必须在synchronized块中调用
                    }
                    System.out.println(&quot;wait/notify: 条件满足，继续执行&quot;);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        
        public void notifyMethod() {
            synchronized (monitor) {
                condition = true;
                System.out.println(&quot;wait/notify: 设置条件为true&quot;);
                monitor.notify(); // 或 monitor.notifyAll()
            }
        }
    }
    
    /**
     * await/signal 机制的特点
     */
    public static class AwaitSignalDemo {
        private final ReentrantLock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();
        private boolean conditionFlag = false;
        
        public void awaitMethod() {
            lock.lock();
            try {
                while (!conditionFlag) {
                    System.out.println(&quot;await/signal: 线程等待中...&quot;);
                    condition.await(); // 必须在lock.lock()后调用
                }
                System.out.println(&quot;await/signal: 条件满足，继续执行&quot;);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
        
        public void signalMethod() {
            lock.lock();
            try {
                conditionFlag = true;
                System.out.println(&quot;await/signal: 设置条件为true&quot;);
                condition.signal(); // 或 condition.signalAll()
            } finally {
                lock.unlock();
            }
        }
        
        /**
         * await/signal的额外功能演示
         */
        public boolean awaitWithTimeout() {
            lock.lock();
            try {
                while (!conditionFlag) {
                    System.out.println(&quot;await/signal: 带超时的等待...&quot;);
                    // 支持超时等待
                    if (!condition.await(1, java.util.concurrent.TimeUnit.SECONDS)) {
                        System.out.println(&quot;等待超时&quot;);
                        return false;
                    }
                }
                return true;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } finally {
                lock.unlock();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;14. 死锁产生的条件是什么？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-38.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;死锁产生的 4 个必要条件：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;互斥，不可剥夺，请求和保持，循环等待&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;⭐怎么预防死锁？&lt;/h2&gt;
&lt;p&gt;从四个条件上来说明，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;破坏互斥？一般来说互斥条件是无法破坏的。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;破坏不可剥夺？可以，当有线程持有了一些资源，并请求新的资源没有得到满足时，那么就&lt;strong&gt;&lt;strong&gt;把已经保持的资源全部释放&lt;/strong&gt;&lt;/strong&gt;，待以后需要使用的时候再重新申请。（会有效率损耗）&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;破坏请求和保持？线程必须&lt;strong&gt;&lt;strong&gt;一次性的请求所有需要使用的全部资源&lt;/strong&gt;&lt;/strong&gt;，可能造成资源利用率低。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;破坏循环等待，可以将每个资源编号，必须&lt;strong&gt;&lt;strong&gt;按照编号顺序&lt;/strong&gt;&lt;/strong&gt;来请求资源。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在数据库层面上呢？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;设置事务等待锁的超时时间&lt;/strong&gt;，在innodb中，设置参数 &lt;code&gt;innodb_lock_wait_timeout&lt;/code&gt; 来设置超时时间&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;开启主动死锁检测&lt;/strong&gt;，检测到死锁后，主动回滚其中一个事务，将 &lt;code&gt;innodb_deadlock_detect&lt;/code&gt; 设置为 on。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;⭐怎么进行死锁诊断？（怎么看程序里有没有死锁）&lt;/h2&gt;
&lt;p&gt;首先使用&lt;strong&gt;jps查看是否有线程发生了死锁&lt;/strong&gt;，定位到对应的threadid，然后用&lt;strong&gt;jstack -l threadid，来查看具体是哪里的代码发生了死锁&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-39.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-40.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;15. 聊一下 ConcurrentHashMap&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1788408712975282177/question/1780933294813114369?screen=full&quot;&gt;Java 中 ConcurrentHashMap 1.7 和 1.8 之间有哪些区别？ &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在jdk8及之后的版本，ConcurrentHashmap是由&lt;strong&gt;数组+链表+红黑树&lt;/strong&gt;构成哈希表，并采用** **&lt;strong&gt;CAS + synchronized&lt;/strong&gt; 来实现更细粒度的并发控制，&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于写put操作，会对key进行hash取值来知道落到哈希表哪个桶，如果这个桶为空，那么会通过&lt;strong&gt;&lt;strong&gt;CAS&lt;/strong&gt;&lt;/strong&gt;存入新的节点。&lt;/strong&gt;
&lt;strong&gt;如果不为空，会对&lt;strong&gt;&lt;strong&gt;头结点进行加synchronize锁&lt;/strong&gt;&lt;/strong&gt;，然后equals判断是否为同一元素，如果是同一个元素，就会替换掉value值，不过不是同一元素，则直接存入。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;然后对于读操作，这个过程几乎是无锁的&lt;/strong&gt;，因此在性能方面会比HashTable高&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-41.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-42.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;只有“空桶”时才用CAS&lt;/strong&gt;（Compare And Swap）尝试无锁插入。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;如果不是空桶&lt;/strong&gt;（即已经有链表/红黑树/已有元素），&lt;strong&gt;直接用synchronized加锁&lt;/strong&gt;，不会用CAS。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果&lt;strong&gt;不同的key哈希到不同的桶，操作互不影响，可以高并发执行&lt;/strong&gt;。只有当&lt;strong&gt;多个线程****操作同一个桶时，才会有锁竞争&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;16. 什么是线程安全？怎么保证线程安全？&lt;/h1&gt;
&lt;p&gt;什么是？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;线程安全&lt;/strong&gt;是指&lt;strong&gt;多个线程访问某一共享资源时，能够保证一致性和正确性，即无论线程如何交替执行，程序都能够产生预期的结果，且不会出现数据竞争或内存冲突&lt;/strong&gt;。在 Java 中，线程安全的实现通常依赖于同步机制和线程隔离。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;同步锁&lt;/strong&gt;：通过 &lt;code&gt;synchronized&lt;/code&gt; 关键字或 &lt;code&gt;ReentrantLock&lt;/code&gt; 实现对共享资源的同步控制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;原子操作类&lt;/strong&gt;：Java 提供的 &lt;code&gt;AtomicInteger&lt;/code&gt;、&lt;code&gt;AtomicReference&lt;/code&gt; 等类确保多线程环境下的原子性操作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;线程安全容器&lt;/strong&gt;：如 &lt;code&gt;ConcurrentHashMap&lt;/code&gt;、&lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; 等，避免手动加锁。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ThreadLocal&lt;/strong&gt;：类似于局部变量，属于线程本地资源，通过线程隔离保证了线程安全。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;局部变量：线程内独立的局部变量天然是线程安全的，因为每个线程都有自己的栈空间（线程隔离）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;怎么保证？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;原子性&lt;/strong&gt;。一个操作是不可分割的，要么全部完成，要么全部不做，中间不能被其他线程干扰。
&lt;strong&gt;使用 synchronized、lock、CAS 解决。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可见性&lt;/strong&gt;。指一个线程对共享变量的修改，能及时被其他线程看到。
&lt;strong&gt;使用 volatile、synchronized、lock 解决。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;有序性&lt;/strong&gt;。指程序执行顺序按照代码的先后顺序进行。在并发环境下，编译器、CPU可能会对指令进行重排序优化，如果缺乏同步措施，实际执行顺序可能与代码书写顺序不一致。
&lt;strong&gt;使用 volatile、synchronized、lock 解决。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;可以看到，锁（synchronized 和 lock）可以保证并发安全的三大特性。&lt;code&gt;synchronized&lt;/code&gt;、&lt;code&gt;ReentrantLock&lt;/code&gt; 等互斥锁，都有这个效果。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;用锁包住的代码块，保证了锁内操作的有序性，不会被重排序到锁外，也不会把锁外操作重排序到锁内。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不仅保证有序性，还保证了可见性和原子性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1&gt;&lt;strong&gt;线程池&lt;/strong&gt;&lt;/h1&gt;
&lt;h1&gt;17. 线程池的执行原理？7大核心参数？&lt;/h1&gt;
&lt;p&gt;核心线程数，总线程数，备用线程存活时间，时间单位，任务阻塞队列（Array还是Linked），线程工厂，拒绝策略&lt;/p&gt;
&lt;p&gt;https://www.yuque.com/kusanagi-mdqmk/kb/a6a655112e42f32fc335066b2df45513#RRI1p&lt;/p&gt;
&lt;p&gt;线程池是一种&lt;strong&gt;是一个线程复用技术，&lt;strong&gt;用于&lt;/strong&gt;预先创建并管理一组线程，避免频繁创建和销毁线程的开销&lt;/strong&gt;，&lt;strong&gt;可以提高线程的利用率&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;线程池的核心参数&lt;/h2&gt;
&lt;p&gt;在JDK5版本中提供了代表&lt;strong&gt;线程池的接口&lt;/strong&gt;&lt;code&gt;ExecutorService&lt;/code&gt;，而这个接口下有一个实现类叫&lt;code&gt;ThreadPoolExecutor&lt;/code&gt;类，使用&lt;code&gt;ThreadPoolExecutor&lt;/code&gt;类就可以用来创建线程池对象。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-43.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-44.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;线程池执行流程？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;流程：来了一个任务，那么首先用核心线程来处理。如果核心线程满了，那么把任务加到阻塞队列里。如果阻塞队列满了，还有备用线程（临时线程），那么就调用备用线程来处理新任务（如果备用线程都没了，那就直接走拒绝策略）。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新来的任务处理完了之后，如果阻塞队列里还有未完成任务，那么核心线程和备用线程都可以去阻塞队列里去处理阻塞中的任务。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;线程池中线程抛异常了会怎样？&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1828365977491333121#heading-0&quot;&gt;Java 线程池内部任务出异常后，如何知道是哪个线程出了异常？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;1.自定义ThreadFactory，给每个线程都搞一个UncaughtExceptionHandler来处理异常&lt;/p&gt;
&lt;p&gt;2.使用Future，使用submit方法提交，get方法的时候try-catch捕获异常&lt;/p&gt;
&lt;p&gt;3.run方法内部，try-catch捕获异常&lt;/p&gt;
&lt;p&gt;execute和submit的区别：&lt;/p&gt;
&lt;p&gt;1.execute无返回值，submit会返回一个future对象；&lt;/p&gt;
&lt;p&gt;2.异常处理区别：
execute：如果任务在执行过程中抛出了未捕获的异常，那么这个异常会被包装成RuntimeException，并在线程终止时由Thread对象的getUncaughtExceptionHandler方法处理；
submit：异常会被捕获并存储在Future对象中，可以通过Future.get()方法重新抛出。&lt;/p&gt;
&lt;p&gt;3.execute只能提交Runnable任务；submit可以以提交Runnable任务 或者 Callable任务&lt;/p&gt;
&lt;p&gt;线程池中的线程在执行任务时，如果抛出异常会怎么样？&lt;/p&gt;
&lt;p&gt;execute提交的任务：如果没有自己处理这个异常，底层会remove掉这个抛异常的线程，然后addWorker一个新的线程。&lt;/p&gt;
&lt;p&gt;submit提交的任务：异常会被捕获并且放在Future对象中，底层不会remove掉这个抛异常的线程，也不会addWorker一个新的线程。&lt;/p&gt;
&lt;h2&gt;任务的拒绝策略&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;直接抛异常。&lt;strong&gt;AbortPolicy，直接抛出 RejectedExecutionException，这是默认的拒绝策略。适用于&lt;/strong&gt;必须通知调用者任务未能被执行的场景&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;直接丢弃该任务。&lt;strong&gt;DiscardPolicy，也不抛异常。适用于&lt;/strong&gt;对部分任务丢弃没有影响的场景&lt;/strong&gt;，或&lt;strong&gt;系统负载高时不需要处理所有任务&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;丢弃任务队列最前的任务。&lt;strong&gt;DiscardOldestPolicy，删除最早的任务，然后重新提交当前任务。适用于&lt;/strong&gt;希望丢弃最旧的任务以保证新的重要任务能够被处理&lt;/strong&gt;的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;由调用者所在线程来执行任务。&lt;strong&gt;CallerRunsPolicy，由调用者线程执行。适用于希望&lt;/strong&gt;通过减缓任务提交速度来稳定系统&lt;/strong&gt;的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-45.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;使用线程池执行任务时，线程池执行的&lt;strong&gt;任务可以有两种&lt;/strong&gt;，一种是&lt;strong&gt;实现****&lt;code&gt;Runnable&lt;/code&gt;&lt;strong&gt;&lt;strong&gt;任务&lt;/strong&gt;；一种是&lt;/strong&gt;实现 ****&lt;code&gt;Callable&lt;/code&gt;&lt;strong&gt;&lt;strong&gt;任务&lt;/strong&gt;。
&lt;code&gt;ThreadPoolExecutor&lt;/code&gt;线程池&lt;/strong&gt;执行任务&lt;/strong&gt;相关方法：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-46.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;public class MyRunnable implements Runnable{
    
    @Override
    public void run() {
        // 任务是干啥的？
        System.out.println(Thread.currentThread().getName() + &quot; ==&gt; 输出666~~&quot;);
        //为了模拟线程一直在执行，这里睡久一点
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

ExecutorService pool = new ThreadPoolExecutor(
    3,        //核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,        //临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行，就会被销毁掉。
    TimeUnit.SECONDS,//时间单位（秒）
    new ArrayBlockingQueue&amp;#x3C;&gt;(4), //任务阻塞队列，没有来得及执行的任务在，任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

Runnable target = new MyRunnable();
pool.execute(target); // 线程池会自动创建一个新线程，自动处理这个任务，自动执行的！
pool.execute(target); // 线程池会自动创建一个新线程，自动处理这个任务，自动执行的！
pool.execute(target); // 线程池会自动创建一个新线程，自动处理这个任务，自动执行的！
//下面4个任务在任务队列里排队
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);

//下面2个任务，会被临时线程的创建时机了
pool.execute(target);
pool.execute(target);
// 到了新任务的拒绝时机了！
pool.execute(target);
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;18. 说说线程池里的阻塞队列有哪些？&lt;/h1&gt;
&lt;p&gt;阻塞队列主要用来阻塞队列的插入和获取操作，当队列满了的时候插入操作会被阻塞，直到队列有空位。当队列为空的时候获取操作会被阻塞，直到队列有值。&lt;/p&gt;
&lt;p&gt;常用在实现生产者和消费者场景，在笔试题中比较常见。&lt;/p&gt;
&lt;p&gt;常见的阻塞队列包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;ArrayBlockingQueue：一个有界队列，底层基于数组实现。需要在初始化时指定队列的大小，队列满时，生产者会被阻塞，队列空时，消费者会被阻塞。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;LinkedBlockingQueue：基于链表的阻塞队列，允许可选的界限（有界或无界）。无界模式下可以不断添加元素，直到耗尽系统资源。有界模式则类似于 &lt;code&gt;ArrayBlockingQueue&lt;/code&gt;，但吞吐量通常较高。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PriorityBlockingQueue：一个无界的优先级队列，元素按照自然顺序或者指定的比较器顺序进行排序。与其他阻塞队列不同的是，PriorityBlockingQueue 不保证元素的 FIFO 顺序。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DelayQueue：一个无界队列，队列中的元素必须实现 &lt;code&gt;Delayed&lt;/code&gt; 接口，只有当元素的延迟时间到期时，才能被取出。常用于延迟任务调度。**是一个&lt;strong&gt;&lt;strong&gt;优先级队列&lt;/strong&gt;&lt;/strong&gt;，任务&lt;strong&gt;&lt;strong&gt;根据它们的延迟时间被组织起来，延迟时间最短的任务排在队列的前面&lt;/strong&gt;&lt;/strong&gt;。这种数据结构允许任务按照预定的时间顺序被取出和执行，**线程池会不断检查队首元素的延迟时间，未到时间就阻塞等待。&lt;strong&gt;用于****定时、延迟任务&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SynchronousQueue：&lt;/strong&gt; 没有容量，不能存储任何元素。它的主要作用是&lt;strong&gt;在线程之间传递数据，即一个线程放入数据，另一个线程取出数据。 有新任务就交给空闲线程执行，没空闲线程就尝试新建线程，否则拒绝。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-47.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ArrayBlockingQueue：一把锁，生产 消费必须串行。性能略低。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;LinkedBlockingQueue：两把锁，put和take各自有锁，生产消费可以并发进行。put和take互不影响，提高了并发性能。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果用无界的 LinkedBlockingQueue，线程池&lt;strong&gt;只会创建核心线程数的线程&lt;/strong&gt;，&lt;strong&gt;不会创建救急线程&lt;/strong&gt;。这可能&lt;strong&gt;导致任务堆积&lt;/strong&gt;，&lt;strong&gt;甚至 OOM（OutOfMemoryError），要慎用无界队列！&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;19. 如何确定线程池的核心线程数？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-48.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h3&gt;CPU 密集型任务&lt;/h3&gt;
&lt;p&gt;CPU 密集型任务，就好比单纯的数学计算任务，它不会涉及 I/O 操作，也就是说它可以充分利用 CPU 资源（如果涉及 I/O，在进行 I/O 的时候 CPU 是空闲的），不会因为 I/O 操作被阻塞，因此不需要很多线程，线程多了上下文开销反而会变多。&lt;/p&gt;
&lt;p&gt;根据经验法则，&lt;code&gt;CPU 密集型任务线程数 = CPU 核心数 + 1&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;I/O 密集型任务&lt;/h3&gt;
&lt;p&gt;I/O 密集型任务，有很多 I/O 操作，例如文件的读取、数据库的读取等等，任务在读取这些数据的时候，是无法利用 CPU 的，对应的线程会被阻塞等待 I/O 读取完成，因此如果任务比较多，就需要有更多的线程来执行任务，来提高等待 I/O 时候的 CPU 利用率。&lt;/p&gt;
&lt;p&gt;根据经验法则，&lt;code&gt;I/O 密集型任务线程数 = CPU 核心数 * 2&lt;/code&gt; 或更多一些。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（这句话一定要和面试官说）以上公式仅是一个纯理论值，仅供参考！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在生产上，需要考虑机器的硬件配置，设置预期的 CPU 利用率、CPU负载等因素，再通过实际的测试不断调整得到合理的线程池配置参数。&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;20. Executors 线程池种类有哪些？&lt;/h1&gt;
&lt;p&gt;**4种。**使用 java.util.concurrent.Executors 类来创建。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;固定线程数&lt;/strong&gt;的线程池（&lt;strong&gt;无临时线程&lt;/strong&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;单线程&lt;/strong&gt;的线程池。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;可缓存&lt;/strong&gt;线程池。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;延迟或周期执行&lt;/strong&gt;的线程池。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-49.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-50.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-51.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-52.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;内部是使用了一个优先级队列，越先执行的任务优先级越高。&lt;/p&gt;
&lt;h1&gt;21. 为什么不推荐 Executors 创建线程池？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-53.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;22. ⭐谈谈对 ThreadLocal 的理解？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-54.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-55.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;介绍一下 ThreadLocal&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ThreadLocal就是&lt;strong&gt;让每个线程都可以存一份自己的数据副本，因此不需要考虑线程安全问题&lt;/strong&gt;，在我做过的项目中一般用来存储用户信息，或者每个线程存放自己的数据库连接。因为后台中每一个请求都是一个线程，我们在后续需要这个数据的时候就可以很方便的获取这个信息。&lt;/p&gt;
&lt;p&gt;而在&lt;strong&gt;每条线程Thread内部有各自的 ThreadLocalMap，内部是一个Entry数组，key值为ThreadLocal对象，value为变量副本。&lt;strong&gt;开发的时候一般把threadLocal声明为一个static静态变量，那么这个时候threadLocal对象就是所有线程共享的，调用 set 和 get 方法的时候，就是以当前的 threadLocal 对象为key，去ThreadLocalMap里去取value。由于&lt;/strong&gt;每条线程Thread内部有各自的 ThreadLocalMap，故不用担心线程安全问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ThreadLocal的内存泄漏问题？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;线程内的ThreadLocalMap里面维护了一个Entry数组&lt;/strong&gt;，&lt;strong&gt;key是&lt;strong&gt;&lt;strong&gt;弱引用&lt;/strong&gt;&lt;/strong&gt;的ThreadLocal，value是数据。如果一个ThreadLocal对象没有强引用来指向它，那么堆内存不足的时候会被GC掉这些弱引用的Key，那么就会出现key为null但是value不为null的情况&lt;/strong&gt;，导致GC无法回收造成内存的泄漏，这个value就是泄漏的对象。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么 key 要设置为弱引用？&lt;/p&gt;
&lt;p&gt;使用弱引用作为 &lt;code&gt;ThreadLocal&lt;/code&gt; 的键可以防止内存泄漏。若 &lt;code&gt;ThreadLocal&lt;/code&gt; 实例被不再需要的线程持有为强引用，那么当该线程结束时，相关的 &lt;code&gt;ThreadLocal&lt;/code&gt; 实例及其对应的数据可能无法被回收，导致内存持续占用。&lt;/p&gt;
&lt;p&gt;而弱引用允许垃圾回收器在内存不足时回收对象。这样，当没有其他强引用指向某个 &lt;code&gt;ThreadLocal&lt;/code&gt; 实例时，它可以被及时回收，避免长时间占用内存。此时 key就变为null了，下次再调用get、set、remove的时候会自动清理key为null的Entry。&lt;/p&gt;
&lt;p&gt;为什么value不设置为弱引用？value值后续可能会用到，如果设置为弱引用，可能会被GC错误回收&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是ThreadLocal在执行get、set、remove的时候会自动清理key为null的Entry，但是&lt;strong&gt;更好的做法还是在使用完ThreadLocal之后调用ThreadLocal中的remove()方法清空该Entry解决该问题&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ThreadLocal原理：&lt;/p&gt;
&lt;p&gt;ThreadLocalMap里面有个Entry数组，Entry的key是ThreadLocal对象，key是通过弱引用指向ThreadLocal对象的&lt;/p&gt;
&lt;p&gt;，value是设置的值。&lt;/p&gt;
&lt;p&gt;get方法：&lt;/p&gt;
&lt;p&gt;get方法会先获取当前线程，然后得到当前线程的ThreadLocalMap，然后将当前的ThreadLocal对象作为key&lt;/p&gt;
&lt;p&gt;从map里面拿到Entry，如果找到的话，直接返回这个entry中的value；&lt;/p&gt;
&lt;p&gt;没有找到的话，就会调用初始化方法，返回初始值。&lt;/p&gt;
&lt;p&gt;getEntry方法: key是传进来的ThreadLocal对象&lt;/p&gt;
&lt;p&gt;通过key计算一个hash值，然后和hashmap一样，模上数组的长度-1，得到一个下标，如果这个下标对应的entry的key正好是&lt;/p&gt;
&lt;p&gt;要找的ThreadLocal对象的话，就表示命中了直接返回；&lt;/p&gt;
&lt;p&gt;没有命中的话，就从当前下标位置，顺序的遍历entry数组去找，找到就返回，找不到就返回null，也就是所谓开放式寻址法。&lt;/p&gt;
&lt;p&gt;set方法：&lt;/p&gt;
&lt;p&gt;根据key算一个hash值，模上数组的长度-1，得到一个下标，&lt;/p&gt;
&lt;p&gt;然后从这个下标对应的entry元素开始，顺序遍历整个entry数组，检查这个下标是不是已经有元素了，如果有的话，对比一下&lt;/p&gt;
&lt;p&gt;是不是要寻找的entry，如果是的话，直接更新entry对应的value；如果不是的话，就继续往下找，&lt;/p&gt;
&lt;p&gt;如果找到一个null值了，说明entry还不存在，就创建一个新的entry。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-56.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-57.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;加锁 lock/同步块synchronized：适合多线程间共享变量，保证同一数据的安全，但有性能损耗。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ThreadLocal：每个线程独立变量，适合“每线程一份状态”场景，无锁高效&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ThreadLocal 类基本使用&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;threadLocal.set(value) 设置值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;threadLocal.get() 获取值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;threadLocal.remove() 清除值&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在实际应用中，一个线程往往需要传递多个上下文信息，比如用户ID、请求ID、数据库事务ID等。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ThreadLocal&lt;/code&gt; 只能存“一个东西”，那我们就让这个“东西”变成一个可以容纳很多其他东西的容器，比如一个 &lt;code&gt;Map&lt;/code&gt; 或者一个自定义的上下文对象（POJO）。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;// 1. ThreadLocal 的泛型是一个 Map
private static final ThreadLocal&amp;#x3C;Map&amp;#x3C;String, Object&gt;&gt; threadLocal = new ThreadLocal&amp;#x3C;&gt;();

public void processRequest() {
    try {
        // 2. 初始化 Map 并存入 ThreadLocal
        Map&amp;#x3C;String, Object&gt; contextMap = new HashMap&amp;#x3C;&gt;();
        contextMap.put(&quot;userId&quot;, &quot;Natsumeaiovo&quot;);
        contextMap.put(&quot;transactionId&quot;, 12345L);
        contextMap.put(&quot;requestId&quot;, &quot;req-xyz-987&quot;);
        
        threadLocal.set(contextMap);

        // ... 在后续的方法调用中，可以随时获取整个上下文
        doSomething();

    } finally {
        // 3. 【极其重要】处理完毕后一定要调用 remove()，防止内存泄漏
        threadLocal.remove();
    }
}

public void doSomething() {
    // 从 ThreadLocal 获取 Map
    Map&amp;#x3C;String, Object&gt; contextMap = threadLocal.get();
    
    // 从 Map 中获取具体的值
    String userId = (String) contextMap.get(&quot;userId&quot;);
    Long transactionId = (Long) contextMap.get(&quot;transactionId&quot;);

    System.out.println(&quot;Processing for user: &quot; + userId + &quot; with txId: &quot; + transactionId);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;各个线程会各自操作 ThreadLocal 提供的变量副本，互不干扰，解决了多线程之间的共享数据竞争问题。&lt;/p&gt;
&lt;h2&gt;ThreadLocal 实现原理&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-58.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-59.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-60.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h2&gt;ThreadLocal 内存泄漏&lt;/h2&gt;
&lt;p&gt;ThreadLocalMap 中的 key 是弱引用，值为强引用；&lt;/p&gt;
&lt;p&gt;Key 会被 GC 释放内存，关联的 value 内存并不会释放。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;建议主动 remove 释放 key，value&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-61.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;23. 线程池 shutdown 与 shutdownNow 的区别是什么？&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;shutdown()&lt;/code&gt; 和 &lt;code&gt;shutdownNow()&lt;/code&gt; 都用于关闭线程池，但工作方式有所不同：&lt;/p&gt;
&lt;p&gt;1）&lt;code&gt;shutdown()&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;启动线程池的&lt;strong&gt;平滑关闭&lt;/strong&gt;。它&lt;strong&gt;不再接受新的任务，但会继续执行已经提交的任务（包括在队列中的任务）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;线程池会进入 &lt;code&gt;SHUTDOWN&lt;/code&gt; 状态，所有已执行和正在执行的任务都会继续完成，只有所有任务完成后，线程池才会完全终止。&lt;/p&gt;
&lt;p&gt;2）&lt;code&gt;shutdownNow()&lt;/code&gt;：&lt;/p&gt;
&lt;p&gt;启动线程池的&lt;strong&gt;强制关闭&lt;/strong&gt;。它会**尝试停止所有正在执行的任务，并返回等待执行的任务列表。它会尽力中断正在执行的任务，但不能保证所有任务都能被立即停止。**线程池进入 &lt;code&gt;STOP&lt;/code&gt; 状态，除了尝试中断正在执行的任务外，还会清空任务队列，返回未执行的任务列表。&lt;/p&gt;
&lt;p&gt;返回的任务队列列表，可以&lt;strong&gt;记录在日志&lt;/strong&gt;或者&lt;strong&gt;提交给另一个线程池&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;shutdownNow会通过thread.interrupt()方法来尝试中断线程，如果被中断的线程没有对中断进行处理的话(如没有检测interrupted状态)，就无法中断。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;shutdown和shutdownNow&lt;strong&gt;多次调用不会有额外效果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;使用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;shutdown()&lt;/code&gt;：适用于程序需要平滑停止线程池的场景，如应用程序正常退出时。保证所有已提交的任务都能执行完毕，避免任务丢失。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;shutdownNow()&lt;/code&gt;：适用于紧急情况或需要立即停止线程池的场景，如程序异常退出时。快速清理资源，但可能导致部分任务未完成。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;因为线程是否能够响应中断请求完全依赖于该线程内部是如何处理 &lt;code&gt;Thread.interrupted()&lt;/code&gt; 方法的。如果一个任务没有检查中断标志或者在长时间的阻塞操作（例如 I/O 操作）中忽略了中断信号，那么即使调用了 &lt;code&gt;shutdownNow()&lt;/code&gt;, 这个任务也可能不会被终止。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;24. 说说CountDownLatch 和 CyclicBarrier 的区别？&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;CountDownLatch是一个线程等其他多个线程完成任务，分-&gt;总关系，例如其他多个线程查询不同分片数据，主线程进行数据聚合&lt;/p&gt;
&lt;p&gt;CyclicBarrier是多个线程同步，一组线程互相等待到某个状态，然后这组线程再同时执行，比如多个线程同时载入缓存，等都载入完成在之后，再多个线程同时进行数据运算。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-62.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-63.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        // 计数器为 3，表示需要等待 3 个任务完成
        CountDownLatch latch = new CountDownLatch(3);

        // 启动 3 个线程来执行任务
        for (int i = 1; i &amp;#x3C;= 3; i++) {
            new Thread(() -&gt; {
                System.out.println(Thread.currentThread().getName() + &quot; 执行任务&quot;);
                latch.countDown();  // 每个线程执行完任务后递减计数器
            }).start();
        }

        System.out.println(&quot;等待所有任务完成...&quot;);
        latch.await();  // 主线程等待所有任务完成
        System.out.println(&quot;所有任务已完成，继续执行主线程&quot;);
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;25. 线程安全的集合有哪些？&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-64.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h1&gt;26. 你用过哪些并发安全类？&lt;/h1&gt;
&lt;p&gt;比如：ConcurrentHashMap、AtomicInteger、Semaphore、CyclicBarrier、CountDownLatch、BlockingQueue 等等。&lt;/p&gt;
&lt;p&gt;这个问题只要把你知道的一些并发类名字说出来就行了，然后等面试官选择其中一个去询问即可（一般需要结合简历中项目的业务场景，所以&lt;strong&gt;需要根据自己的业务提前准备&lt;/strong&gt;）。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我使用了 CompletebleFuture，在OJ项目里用它异步去执行判题操作。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294951526402#heading-0&quot;&gt;什么是 Java 的 CompletableFuture？&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;// 执行判题服务
Long questionSubmitId = questionSubmit.getId();
CompletableFuture.*runAsync*(() -&gt; judgeService.doJudge(questionSubmitId));
return questionSubmitId;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;我使用了 ConcurrentHashMap 作为本地缓存Cache，用来存放热点视频信息，key 为 videoId，value 为对应的 VideoInfo，保存了视频的各种信息。在每次查询的时候，先判断该videoId是否在Cache里，在就直接返回视频信息；否则判断该 videoId 是否在当前的热门视频列表中（怎么判断见下），如果在就查数据库并将该视频信息加入到 Cache。&lt;/p&gt;
&lt;p&gt;热门视频列表每小时更新一次，通过一个定时任务（@EnableScheduling），来排序24小时内视频的播放量，作为score存到redis的zset，这个zset的前100名就是当前的热点视频id，该定时任务也会去检查Cache里的videoId是否是热点id，如果不是则remove。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;import java.util.concurrent.ConcurrentHashMap;

@Service(&quot;videoInfoService&quot;)
public class VideoInfoServiceImpl implements VideoInfoService {

    @Resource
    private VideoInfoMapper videoInfoMapper;
    
    // 假设有 RedisComponent 用于操作 Redis 缓存
    @Resource
    private RedisComponent redisComponent;

    // 使用 ConcurrentHashMap 作为视频信息的本地缓存
    // Key: videoId, Value: VideoInfo 对象
    private final ConcurrentHashMap&amp;#x3C;String, VideoInfo&gt; videoInfoCache = new ConcurrentHashMap&amp;#x3C;&gt;();

    @Override
    public VideoInfo getVideoInfoByVideoId(String videoId) {
        // 1. 优先从本地缓存获取
        VideoInfo videoInfo = videoInfoCache.get(videoId);
        if (videoInfo != null) {
            return videoInfo;
        }

        // 2. 未命中，从数据库查询
        videoInfo = videoInfoMapper.selectByVideoId(videoId); // 假设有这个查询方法
        if (videoInfo != null) {
            // 将从数据库查到的数据放入本地缓存
            videoInfoCache.put(videoId, videoInfo);
        }
        return videoInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVideoInfo(VideoInfo videoInfo) {
        // 更新数据库
        videoInfoMapper.updateByVideoId(videoInfo);
        
        // 关键：当视频信息更新后，从本地缓存中移除旧数据
        // 这样下次调用 getVideoInfoByVideoId 时就会重新从数据库加载最新数据
        videoInfoCache.remove(videoInfo.getVideoId());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;我使用了 AtomicLong原子类来统计每天网站的访问量，每次调用login或autoLogin的时候就会去增加计数，每天0点清零。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我使用了CountDownLatch。在我的 VideoCommentController 的 loadComment 方法中，需要&lt;strong&gt;加载评论列表，同时还需要查询当前登录用户对当前视频下评论的点赞/点踩状态&lt;/strong&gt;。这两个操作可以并行执行，执行完了就latch.countDown()，主线程等待latch计数器变0，再汇总成VO，以缩短总响应时间。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;@RequestMapping(&quot;/loadComment&quot;)
public ResponseVO loadComment(@NotEmpty String videoId, Integer pageNo, Integer orderType) {
    
    final CountDownLatch latch = new CountDownLatch(2); // 初始化计数器为2，因为有两个并行任务
    
    final PaginationResultVO&amp;#x3C;VideoComment&gt;[] commentDataContainer = new PaginationResultVO[1];
    final List&amp;#x3C;UserAction&gt;[] userActionListContainer = new List[1];

    // 任务1：加载评论列表
    executorService.submit(() -&gt; {
        try {
            // ... 这里是加载评论、处理置顶评论的全部逻辑 ...
            PaginationResultVO&amp;#x3C;VideoComment&gt; commentData = videoCommentService.findListByPage(commentQuery);
            // ... 处理置顶评论的逻辑 ...
            commentDataContainer[0] = commentData;
        } finally {
            latch.countDown(); // 任务1完成，计数器减1
        }
    });

    // 任务2：加载用户行为
    executorService.submit(() -&gt; {
        try {
            List&amp;#x3C;UserAction&gt; userActionList = new ArrayList&amp;#x3C;&gt;();
            TokenUserInfoDto tokenUserInfoDto = getCurTokenUserInfoDto();
            if (tokenUserInfoDto != null) {
                // ... 查询用户点赞/点踩记录的逻辑 ...
                userActionList = userActionService.findListByParam(actionQuery);
            }
            userActionListContainer[0] = userActionList;
        } finally {
            latch.countDown(); // 任务2完成，计数器减1
        }
    });

    try {
        // 主线程等待，直到 latch 计数器变为0
        latch.await(5, TimeUnit.SECONDS); // 设置一个超时时间，防止永久等待
    } catch (InterruptedException e) {
        log.error(&quot;加载评论时等待子任务被中断&quot;, e);
        // 可以考虑抛出异常或返回错误信息
    }

    // 汇总结果
    VideoCommentResultVO resultVO = new VideoCommentResultVO();
    resultVO.setCommentData(commentDataContainer[0]);
    resultVO.setUserActionList(userActionListContainer[0]);
    return getSuccessResponseVO(resultVO);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;我使用了synchronize，up主在对&lt;strong&gt;评论进行置顶&lt;/strong&gt;的时候，需要首先将 （1.已置顶的评论取消置顶，2.再将选中评论置顶），需要保证这个过程的原子性，不能出现两个评论同时置顶，所以在这段逻辑上加了synchronize。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;synchronized (videoId.intern()) {
    // 1. 先把该视频下已有的置顶评论取消置顶
    VideoComment updateComment = new VideoComment();
    updateComment.setTopType(CommentTopTypeEnum.NO_TOP.getType());
    VideoCommentQuery videoCommentQuery = new VideoCommentQuery();
    videoCommentQuery.setVideoId(videoId);
    videoCommentQuery.setTopType(CommentTopTypeEnum.TOP.getType());
    videoCommentMapper.updateByParam(updateComment, videoCommentQuery);

    // 2. 置顶本评论 (保留您原有的调用方式)
    videoCommentMapper.updateCountInfo(commentId, &quot;top_type&quot;, 1, null);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;扩展知识&lt;/h2&gt;
&lt;h3&gt;1. ConcurrentHashMap&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 是一个线程安全且高效的哈希表，支持并发访问。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 多个线程可以同时进行读写操作，而不会导致线程安全问题。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;ConcurrentHashMap&amp;#x3C;String, Integer&gt; map = new ConcurrentHashMap&amp;#x3C;&gt;();
map.put(&quot;key1&quot;, 1);
Integer value = map.get(&quot;key1&quot;);
map.computeIfAbsent(&quot;key2&quot;, k -&gt; 2);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. AtomicInteger&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 提供一种线程安全的方式对 &lt;code&gt;int&lt;/code&gt; 类型进行原子操作，如增减、比较。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 适用于需要频繁对数值进行无锁操作的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet(); *// 递增*
atomicInt.decrementAndGet(); *// 递减*
atomicInt.compareAndSet(1, 2); *// 比较并设置*
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Semaphore&lt;/h3&gt;
&lt;p&gt;定义几个信号灯，acquire到信号灯的线程才能运行，acquire到一次信号灯，信号灯数量减1，信号灯数量到0之后，后面的线程就acquire不到了。&lt;/p&gt;
&lt;p&gt;适用场景：限流。作用：控制同时运行的线程的数量&lt;/p&gt;
&lt;p&gt;支持公平与非公平锁，通过第二个参数fair设置。公平锁一个线程来了会先去队列里面看看有没有别的线程在，线程按请求许可的顺序获取许可。非公平，顺序不保证。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 控制访问资源的线程数，可以用来实现限流或访问控制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 在资源有限的情况下，&lt;strong&gt;控制同时访问的线程数量。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;Semaphore semaphore = new Semaphore(3);
try {
    semaphore.acquire(); *// 获取许可*
    *// 执行任务。。。*
} finally {
    semaphore.release(); *// 释放许可*
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. CyclicBarrier&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 让一组线程到达一个共同的同步点，然后一起继续执行。常用于分阶段任务执行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 适用于需要所有线程在某个点都完成后再继续的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-190916-65.png&quot; alt=&quot;image.png&quot;&gt;&lt;/p&gt;
&lt;h3&gt;5. CountDownLatch&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 一个线程（或多个）等待其他线程完成操作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 适用于主线程需要等待多个子线程完成任务的场景。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;CountDownLatch latch = new CountDownLatch(3);
Runnable task = () -&gt; {
    try {
        *// 执行任务*
    } finally {
        latch.countDown(); *// 任务完成，计数器减一*
    }
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
latch.await(); *// 等待所有任务完成*
System.out.println(&quot;所有任务都完成了&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. BlockingQueue&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;作用： 是一个线程安全的队列，支持阻塞操作，适用于生产者-消费者模式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用法： 生产者线程将元素放入队列，消费者线程从队列中取元素，队列为空时消费者线程阻塞。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-Java&quot;&gt;BlockingQueue&amp;#x3C;String&gt; queue = new LinkedBlockingQueue&amp;#x3C;&gt;();
Runnable producer = () -&gt; {
    try {
        queue.put(&quot;item&quot;); *// 放入元素*
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};
Runnable consumer = () -&gt; {
    try {
        String item = queue.take(); *// 取出元素*
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};
new Thread(producer).start();
new Thread(consumer).start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;关联题目&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294934749186&quot;&gt;什么是 Java 的 Semaphore？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294938943489&quot;&gt;什么是 Java 的 CyclicBarrier？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294943137794&quot;&gt;什么是 Java 的 CountDownLatch？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294947332098&quot;&gt;什么是 Java 的 StampedLock？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1780933294951526402&quot;&gt;什么是 Java 的 CompletableFuture？&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mianshiya.com/bank/1789249312885223425/question/1864641723497508866#heading-0&quot;&gt;Java 线程安全的集合有哪些? &lt;/a&gt;&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>开封旅游小记</title><link>https://natsumeaiovo.github.io/blog/260616-kaifeng</link><guid isPermaLink="true">https://natsumeaiovo.github.io/blog/260616-kaifeng</guid><description>开封3日游</description><pubDate>Tue, 16 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;确实是挺热的，但是还是玩的很开心😊&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-223428.png&quot; alt=&quot;image-20260726223428075&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-223446.png&quot; alt=&quot;image-20260726223445626&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-223500.png&quot; alt=&quot;image-20260726223459477&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-223523.png&quot; alt=&quot;image-20260726223522903&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/astro-blog/2026/07/26/20260726-223545.png&quot; alt=&quot;image-20260726223545312&quot;&gt;&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>2022新年快乐</title><link>https://natsumeaiovo.github.io/blog/220226-happy-new-year2022</link><guid isPermaLink="true">https://natsumeaiovo.github.io/blog/220226-happy-new-year2022</guid><description>2022年新年</description><pubDate>Sat, 26 Feb 2022 10:20:30 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/img/image-20220201002705612.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/img/image-20220201002738797.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/img/image-20220201002759448.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/img/image-20220201002831626.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://kusanagi.oss-cn-beijing.aliyuncs.com/img/image-20220201103327465.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;</content:encoded><h:img src="https://kusanagi.oss-cn-beijing.aliyuncs.com/img/newyear2022index.webp"/><enclosure url="https://kusanagi.oss-cn-beijing.aliyuncs.com/img/newyear2022index.webp"/></item></channel></rss>