多线程能做什么

当一个任务内容比较多的时候,我们尝试着将任务进行拆解,将整个任务拆分成为多个子任务,交给多个线程各自去完成,这时候就引入了多线程的概念。

既然引入了多线程,那么就会带来相应的线程管理的问题。比如不分事情需要有绝对的先后顺序,而线程执行的时机是不确定的,因此我们必须要想办法控制A在B之前执行。这时候就需要有锁的概念。在次之前,而此处还必须从线程的基本概念开始。

JAVA中怎么创建线程

⭐️ 继承Thread

public class ThreadExtendDemo extends Thread {

    @Override
    public void run() {

        System.out.println("线程执行方法...");
    }
}

public class Main {
    public static void main(String[] args) {
        new ThreadExtendDemo().start();
        System.out.println("主线程执行方法....");
    }
}

此处需要注意,如果在main方法中直接调用线程的run方法,这便不是启动线程了,而是显式的的方法调用。

⭐️实现Runnable接口

public class ThreadImplementDemo implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable开启线程....");
    }
}

public class Main {
    public static void main(String[] args) {
        // new ThreadExtendDemo().start();
        // System.out.println("主线程执行方法....");
        new Thread(new ThreadImplementDemo()).start();
        System.out.println("主线程执行方法....");
    }
}

▲两种方法在写法上有区别,但是原理都是一样的,因为Thread类本身就是实现了Runnable接口的
在这里插入图片描述
另外,在某些场合,我们需要线程执行后,将结果返回,方便后续对各个结果进行后置处理。在Java中,CallableFuture可以实现实时获取到线程的执行结果。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableDemo implements Callable<String> { // 建议指定泛型类型,这里是String

  @Override
  public String call() throws Exception { // 返回值类型与泛型一致
    System.out.println("Callable 线程执行...");
    // 模拟耗时操作
    Thread.sleep(2000);
    return "callable finished";
  }

  public static void main(String[] args) {
    // 1. 创建Callable实现类的实例
    CallableDemo callable = new CallableDemo();

    // 2. 创建线程池(这里使用单线程池,也可以用其他类型)
    ExecutorService executor = Executors.newSingleThreadExecutor();

    // 3. 提交Callable任务到线程池,返回Future对象(用于获取结果)
    Future<String> future = executor.submit(callable);

    try {
      // 4. 通过Future获取线程执行结果(会阻塞等待结果返回)
      String result = future.get();
      System.out.println("线程返回结果:" + result);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // 5. 关闭线程池
      executor.shutdown();
    }
  }
}

Demo中是使用了线程池来完成的,如果不使用线程池,则可以通过FutureTask来完成:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableDemo implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("Callable 线程执行...");
        Thread.sleep(2000); // 模拟耗时操作
        return "callable finished";
    }

    public static void main(String[] args) {
        // 1. 创建Callable实例
        CallableDemo callable = new CallableDemo();

        // 2. 用FutureTask包装Callable(FutureTask实现了Runnable接口)
        FutureTask<String> futureTask = new FutureTask<>(callable);

        // 3. 用Thread包装FutureTask并启动
        Thread thread = new Thread(futureTask);
        thread.start();

        try {
            // 4. 通过FutureTask获取返回结果
            String result = futureTask.get();
            System.out.println("线程返回结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

题外话:本人也写过golang,这里的Future获取线程执行结果的过程,在golang中也有相似的做法,如channel和groutine

package main

import (
	"fmt"
	"time"
)

// 定义一个函数,模拟需要返回结果的任务
// 函数参数中包含一个用于传递结果的channel
func callableTask(resultChan chan<- string) {
	fmt.Println("goroutine 执行中...")
	// 模拟耗时操作
	time.Sleep(2 * time.Second)
	// 将结果发送到channel中
	resultChan <- "callable finished"
}

func main() {
	// 1. 创建一个用于接收结果的channel(字符串类型)
	resultChan := make(chan string)

	// 2. 启动goroutine(相当于Java的线程),并传入结果channel
	go callableTask(resultChan)

	// 3. 从channel中获取结果(会阻塞等待,直到有数据到来)
	result := <-resultChan
	fmt.Println("goroutine返回结果:", result)

	// 4. 关闭channel(可选,这里简单场景可以省略)
	close(resultChan)
}

在 Go 语言中,可以使用通道(channel) 来实现类似 Java 中Callable获取线程(Go 中称为 goroutine)返回结果的功能。通道是 Go 语言中用于 goroutine 之间通信的核心机制,天然来传递数据和同步操作

线程的生命周期

主线程执行方法....
实现Runnable开启线程....

Process finished with exit code 0

从线程的输出结果来看,此处是开启了两个线程,一个main线程和一个子线程。
如果我们开启多个线程,就会出现处于多种状态下的线程。

线程的状态

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

从以上源码中可以看到,线程一共有六种状态,分别是NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,而从注释中可以看到:

线程的状态可以通过以下方法查看:

  • jps
  • jstack PID

NEW

线程创建了,但是未执行start方法。

     * Thread state for a thread which has not yet started.
     */
public class NewStateDemo {
  public static void main(String[] args) throws InterruptedException {
    // 创建线程对象,但不调用start()方法
    Thread newThread = new Thread(() -> {
      System.out.println("线程执行中..."); // 这行代码永远不会执行,因为线程未启动
    }, "NEW-Thread");

    // 打印线程状态(此时应为NEW)
    System.out.println("线程状态: " + newThread.getState());

    // 主线程休眠足够长时间,方便用jstack查看
    Thread.sleep(1000 * 60 * 5); // 休眠5分钟
  }
}

输出

线程状态: NEW

NEW状态比较特殊,处于NEW状态的线程尚未被 JVM 调度初始化,因此不会出现在jstack的线程列表中,因此只能通过代码中获取状态。

RUNNABLE

运行中的状态。

    /**
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
public class RunnableStateDemo {
  public static void main(String[] args) throws InterruptedException {
    // 创建并启动线程(启动后进入Runnable状态)
    Thread runnableThread = new Thread(new RunnableTask(), "Runnable-Thread");
    runnableThread.start();

    // 主线程休眠,保持程序运行
    Thread.sleep(1000 * 60 * 5); // 休眠5分钟
  }

  // 执行持续运行的任务,让线程保持在Runnable状态
  static class RunnableTask implements Runnable {
    @Override
    public void run() {
      // 无限循环执行轻量任务(不阻塞,不等待)
      long count = 0;
      while (true) {
        // 简单的计数操作,避免线程进入阻塞状态
        count++;
        // 每计数1亿次打印一次,证明线程在运行
        if (count % 100_000_000 == 0) {
          System.out.println("线程运行中,计数: " + count);
        }
      }
    }
  }
}

D:\Data\JAVA\code\demo1>jps
22672
7808 Jps
29268 Main
16328 RunnableStateDemo
27868 KotlinCompileDaemon
29068 Launcher

D:\Data\JAVA\code\demo1>jstack 16328
2025-09-27 13:45:37
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"Runnable-Thread" #20 prio=5 os_prio=0 tid=0x0000000028bb9800 nid=0x5d98 runnable [0x000000002ba1e000]
   java.lang.Thread.State: RUNNABLE
        at RunnableStateDemo$RunnableTask.run(RunnableStateDemo.java:21)
        at java.lang.Thread.run(Thread.java:748)

从jstack的结果中可以看到,线程Runnable-Thread正处于RUNNABLE状态,而从代码中可以看到,线程并没有阻塞,而是在不断地执行计算。

猜测此处命名为RUNNABLE而不是RUNNING,原因在于线程的调度,CPU也不知道线程什么时候能获取到资源,从一个就绪状态转为运行状态。

BLOCKED

线程处于阻塞状态,等待被调用notify|notifyAll方法唤醒。

     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
public class BlockStateDemo {
  // 定义一个共享锁对象
  private static final Object LOCK = new Object();
  public static void main(String[] args) {

    Thread t1 = new Thread(new LockTask(),"持有锁的线程");
    Thread t2 = new Thread(new LockTask(), "持有锁的线程");
    t1.start();
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    t2.start();
  }

  static  class LockTask implements Runnable{

    @Override
    public void run() {
     synchronized (LOCK) {
       while (true) {

       }
     }
    }
  }
}

D:\Data\JAVA面试班\code\demo1>jps
22672
30016 Jps
29268 Main
31220 BlockStateDemo
27868 KotlinCompileDaemon
30796 Launcher

D:\Data\JAVA面试班\code\demo1>jstack 31220
2025-09-27 14:12:51
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"DestroyJavaVM" #22 prio=5 os_prio=0 tid=0x0000000002d02800 nid=0x5860 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"持有??的线程" #21 prio=5 os_prio=0 tid=0x000000002835d000 nid=0x786c waiting for monitor entry [0x000000002b2cf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at BlockStateDemo$LockTask.run(BlockStateDemo.java:22)
        - waiting to lock <0x00000007164c48a8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

"持有锁的线程" #20 prio=5 os_prio=0 tid=0x00000000282ab800 nid=0x70e0 runnable [0x000000002b1cf000]
   java.lang.Thread.State: RUNNABLE
        at BlockStateDemo$LockTask.run(BlockStateDemo.java:22)
        - locked <0x00000007164c48a8> (a java.lang.Object)

从结果上看,“持有锁的线程”获取到锁之后,处于无限循环(演示用,勿用于生产)中,因此另一个线程则永远无法获取到锁,因此会处于BLOCKED阻塞状态。

此处有设计到JVM的相关优化:锁的升级,后边会有升级机制的介绍。

WAITING&TIMED_WAITING

从注释中可以看出,通过调用wait(),join(),park()方法,可以让线程进入到WAITING状态,此处需要注意的是,调用join方法指的是不带超时时间的,如果是带了超时时间,则会进入到TIMED_WAITING状态

/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*


  • *
  • {@link Object#wait() Object.wait} with no timeout

  • *
  • {@link #join() Thread.join} with no timeout

  • *
  • {@link LockSupport#park() LockSupport.park}

  • *

*
*

A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/

通过wait进入
public class WaitingStateDemo {
    // 共享的锁对象
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 创建并启动线程
        Thread waitingThread = new Thread(new WaitingTask(), "WAITING-Thread");
        waitingThread.start();

        // 主线程休眠,保持程序运行
        Thread.sleep(1000 * 60 * 5); // 休眠5分钟
    }

    static class WaitingTask implements Runnable {
        @Override
        public void run() {
            synchronized (LOCK) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 获得锁,准备进入WAITING状态");
                    // 调用wait()进入无限等待(无超时时间)
                    LOCK.wait(); // 没有其他线程唤醒,会一直处于WAITING状态
                    // 以下代码永远不会执行(除非被唤醒)
                    System.out.println(Thread.currentThread().getName() + " 被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
通过park进入
import java.util.concurrent.locks.LockSupport;

public class ParkWaitingDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建并启动线程
        Thread parkThread = new Thread(new ParkTask(), "Park-Waiting-Thread");
        parkThread.start();

        // 主线程休眠,保持程序运行
        Thread.sleep(1000 * 60 * 5); // 休眠5分钟
    }

    static class ParkTask implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 准备调用park()进入等待状态");
            
            // 调用park()使线程进入WAITING状态
            LockSupport.park(); // 没有其他线程调用unpark(),会一直等待
            
            // 以下代码只有被unpark()唤醒后才会执行
            System.out.println(Thread.currentThread().getName() + " 被unpark()唤醒");
        }
    }
}

通过join进入
public class JoinWaitingDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建子线程
        Thread childThread = new Thread(new LongRunningTask(), "子线程");
        childThread.start();

        System.out.println("主线程调用join(),开始等待子线程完成");
        
        // 主线程调用子线程的join(),会进入WAITING状态直到子线程结束
        childThread.join(); // 无超时参数,会无限等待
        
        // 以下代码只有子线程执行完毕后才会执行
        System.out.println("子线程执行完毕,主线程继续执行");
    }

    // 长时间运行的任务,让主线程长时间处于等待状态
    static class LongRunningTask implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("子线程开始执行,将运行5分钟");
                // 子线程休眠5分钟,模拟长时间任务
                Thread.sleep(1000 * 60 * 5);
                System.out.println("子线程执行完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
调用join方法后,主线程会进入WAITNG状态,而子线程因为内部有sleep方法,且带了超时时间,会进入TIMED_WAITNG状态。

TERMINATED

线程完整执行完任务会进入改状态,同时,执行interupt方法也可以进入改状态。

    /**
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
public class InterruptTerminatedDemo {
    public static void main(String[] args) throws InterruptedException {
        // 创建线程,执行可中断的任务
        Thread taskThread = new Thread(new InterruptibleTask(), "可中断线程");
        taskThread.start();

        // 主线程休眠1秒,确保子线程进入运行状态
        Thread.sleep(1000);

        // 调用interrupt()中断子线程
        System.out.println("主线程调用interrupt()中断子线程");
        taskThread.interrupt();

        // 等待子线程终止
        Thread.sleep(1000);

        // 打印子线程最终状态(应为TERMINATED)
        System.out.println("子线程最终状态: " + taskThread.getState());
    }

    static class InterruptibleTask implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("子线程开始执行,进入循环任务");
                // 模拟长时间运行的任务(可响应中断)
                while (!Thread.currentThread().isInterrupted()) {
                    // 执行任务逻辑(此处用休眠模拟)
                    Thread.sleep(500);
                    System.out.println("子线程执行中...");
                }
                // 检测到中断信号后退出循环
                System.out.println("子线程检测到中断信号,准备退出");
            } catch (InterruptedException e) {
                // 捕获sleep()被中断的异常
                System.out.println("子线程在休眠中被中断,准备退出");
                // 可根据需要重新设置中断状态(视业务场景而定)
                // Thread.currentThread().interrupt();
            }
            // run()方法执行完毕,线程进入TERMINATED状态
        }
    }
}

进入TERMINATED状态有两种方式,一种是正常结束,另一种是被显示调用了interupte方法。

特别需要注意的是
interrupt()方法并不会立即终止线程,它的核心作用是给线程发送一个 “中断信号”(设置线程的中断标志位为true)。线程是否终止、何时终止,完全由线程自身的逻辑决定 —— 需要线程主动检测这个信号并做出响应。
当线程处于sleep()等阻塞状态时,它能够接收中断信号,此时interrupt()会立即唤醒线程并抛出InterruptedException(这是阻塞状态下对中断的特殊响应),但线程并不会因此 “被强行终止”,是否后续是否继续运行还是终止,取决于异常处理逻辑(例如在catch块中主动退出run()方法才会终止)。
因此,想要优雅地结束线程,应当在代码中结合Thread.currentThread().isInterrupted()(实例方法,仅返回中断状态,不清除标志位)进行判断,在检测到中断信号时主动退出任务。需要注意的是,Thread.interrupted()是静态方法,它会返回当前线程的中断状态并清除中断标志位(复位为false),使用时需区分两者的差异。

wait和park的区别

上方有提到wait和park方法可以让线程进入到BLOCKED状态,但两者之间是否有区别呢?
wait()(来自Object类)和park()(来自LockSupport类)都是Java中用于线程等待/阻塞的方法,但在使用场景、实现机制和细节上有显著区别,主要差异如下:

1. 所属类与调用条件

  • wait()

    • 属于Object类,是所有对象都具备的方法。
    • 必须在synchronized同步块/方法中调用(必须先获取对象的监视器锁),否则会抛出IllegalMonitorStateException
    • 示例:
      synchronized (lock) {
          lock.wait(); // 必须在同步块内调用
      }
      
  • park()

    • 属于LockSupport工具类,是静态方法。
    • 无需获取任何锁,可在任意位置调用。
    • 示例:
      LockSupport.park(); // 直接调用,无需锁
      

2. 唤醒方式

  • wait()

    • 必须由其他线程调用相同对象的notify()notifyAll()唤醒。
    • 唤醒后,线程需要重新竞争获取锁才能继续执行(可能再次阻塞)
  • park()

    • 需由其他线程调用LockSupport.unpark(Thread)唤醒(指定具体线程)。
    • 唤醒后直接继续执行,无需重新竞争锁(无锁阻塞)

3. 中断响应

  • wait()

    • 被中断时会抛出InterruptedException,且会清除中断标志位。
    • 示例:
      try {
          lock.wait();
      } catch (InterruptedException e) {
          // 中断后进入此处,中断标志位已被清除
          System.out.println("被中断: " + Thread.currentThread().isInterrupted()); // false
      }
      
  • park()

    • 被中断时不会抛出异常,但会清除中断标志位(需手动检测)。
    • 示例:
      LockSupport.park();
      // 被中断后继续执行,需手动检查
      if (Thread.interrupted()) { 
          System.out.println("被中断"); // 此处会清除标志位
      }
      

4. 超时机制

  • wait()

    • 支持超时版本:wait(long timeout)(毫秒),超时后自动唤醒。
    • 示例:lock.wait(1000); // 最多等待1秒
  • park()

    • 支持超时版本:parkNanos(long nanos)(纳秒)和parkUntil(long deadline)(绝对时间)。
    • 示例:LockSupport.parkNanos(1_000_000_000); // 最多等待1秒

5. 等待队列归属

  • wait()

    • 线程会进入锁对象的等待队列,与锁绑定。
    • 唤醒后只能竞争当前锁对象。
  • park()

    • 线程会进入自身的等待状态,与具体锁无关。
    • 唤醒后可继续执行任意逻辑,不受锁约束。

6. 典型使用场景

  • wait()

    • 传统同步机制(synchronized)中实现线程间协作(如生产者-消费者模型)。
  • park()

    • 并发工具类底层实现(如ReentrantLockCountDownLatch),更灵活的线程阻塞控制。

总结对比表

特性 wait() park()
所属类 Object LockSupport
是否需要锁 必须(synchronized块内) 不需要
唤醒方式 notify()/notifyAll() unpark(Thread)
中断响应 InterruptedException 不抛异常,需手动检测
锁竞争 唤醒后需重新竞争锁 唤醒后直接执行,无锁竞争
典型应用 传统同步机制协作 并发工具类底层实现

锁升级

在 Java 中,锁升级(Lock Escalation)是 JVM 为了优化同步性能而采用的一种自适应锁机制,主要针对synchronized关键字实现的同步锁。
其核心思想是:根据锁的竞争程度,将锁从低开销的形式逐步升级为高开销但更适合并发的形式,以此平衡性能和线程安全。

锁的四种状态(升级路径)

Java 中的synchronized锁存在四种状态,按照升级顺序依次为:

  • 无锁状态 (Unlocked)
    锁对象刚创建时,未被任何线程持有,处于无锁状态。

  • 偏向锁(Biased Locking)
    适用场景:当锁仅被一个线程多次获取,且无竞争时。
    原理:锁对象头会记录当前持有锁的线程 ID,后续该线程再次获取锁时,无需通过 CAS 竞争,只需判断线程 ID 是否匹配,几乎无开销。
    升级触发:当有第二个线程尝试获取锁时,偏向锁会升级为轻量级锁。

  • 轻量级锁(Lightweight Locking)
    适用场景:多个个线程交替获取锁(竞争不激烈)。
    原理:线程获取锁时,会通过 CAS(CompareAndSet:这是一个原子操作) 操作在锁对象头中记录 “锁记录” 指针(指向线程栈中的锁记录),避免了重量级锁的内核态切换。
    升级触发:当多个线程同时竞争锁(出现锁冲突),且自旋(忙等)次数超过阈值时,轻量级锁会升级为重量级锁。

  • 重量级锁(Heavyweight Locking)
    适用场景:多线程激烈竞争锁(如高并发场景)。
    原理:依赖操作系统的互斥量(Mutex)实现,线程竞争失败时会进入阻塞状态(内核态),等待操作系统调度唤醒,开销较大。

锁升级的核心逻辑

1、自适应:
JVM 会根据历史竞争情况动态调整锁状态(例如,若偏向锁经常被多个线程竞争,可能直接取消偏向模式)。

2、不可逆:
锁升级是单向的(无锁→偏向锁→轻量级锁→重量级锁),一旦升级为重量级锁,不会再降级,以避免频繁切换的开销。
为什么需要锁升级?

3、减少开销:
低竞争场景下,偏向锁和轻量级锁通过用户态操作(避免内核态切换)显著提升性能。

**4、兼顾并发:
**高竞争场景下,重量级锁通过操作系统机制保证线程安全,避免自旋浪费 CPU 资源。
这种设计让synchronized在 JDK 1.6 之后性能大幅提升,成为 Java 中高效的同步机制。

一个例子看懂JAVA锁升级机制

用 “公共卫生间” 的场景来类比 Java 中的锁升级机制,帮助理解不同锁状态的切换过程:

1. 无锁状态

卫生间刚打扫干净,门没锁,也没人使用。此时任何想使用的人都可以直接进入 —— 对应无锁状态,对象未被任何线程持有。

2. 偏向锁

假设小明第一个来上厕所,他进去后没有锁门(但心里默认 “这是我正在用的”),并且在门上贴了一张纸条:“当前使用者:小明”。
小明用完出来后,没撕纸条就走了。下次他再来时,看到纸条上是自己的名字,直接推门就进,无需重新 "登记"—— 这就是偏向锁的核心:同一线程重复获取锁时,几乎无开销。
此时如果只有小明一个人用这个卫生间,纸条会一直贴着,永远是 “偏向” 小明的状态。

3. 轻量级锁(自旋锁)

某天小红也来用卫生间,发现门上贴着 “小明” 的纸条(偏向锁)。这时小红会撕掉纸条,和小明 “协商”:“我先用一下,你稍等”。
小明如果刚好用完出来了,小红就进去,并在门上贴自己的纸条;
如果小明还在里面,小红不会立刻走,而是在门口等几秒(自旋),看看小明是不是很快就出来 —— 这就是轻量级锁:线程竞争不激烈时,通过短时间等待(自旋)避免 "重量级" 的排队流程

4. 重量级锁

如果卫生间门口突然来了很多人(高并发),小红等了几秒(自旋次数阈值)发现小明还没出来,后面又排了一堆人。这时大家意识到 “协商” 没用了,需要一个明确的排队机制
于是有人搬来一个叫号机,所有人必须取号排队,轮到自己才能进去,没轮到的人可以去做别的事(不会一直等)—— 这就是重量级锁:通过操作系统级别的排队机制(类似互斥量)管理竞争,虽然开销大,但适合高并发场景。


Studying will never be ending.

▲如有纰漏,烦请指正~~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐