跳至主要內容
JDK 虚拟线程完全指南:原理、使用与常见问题

前言

JDK 21 正式将虚拟线程(Virtual Threads)作为一项特性发布,彻底改变了 Java 并发编程的格局。传统平台线程在面对高并发 I/O 密集型场景时,线程数受限于操作系统资源,开发者不得不借助异步编程或响应式框架来突破瓶颈。虚拟线程的出现让"每个请求一个线程"的简洁模型重新焕发生机——但用不对也会踩坑。

本文将系统梳理虚拟线程的原理、与平台线程的区别、正确使用方式以及常见问题。


1. 什么是虚拟线程

虚拟线程是由 JDK 而非操作系统管理的轻量级线程。它的工作原理可以概括为:

  1. 虚拟线程是用户态线程:由 JVM 调度,不直接映射到操作系统线程
  2. 载体线程(Carrier Thread):虚拟线程需要挂载到一个平台线程上才能执行
  3. 自动挂起/恢复:当虚拟线程遇到阻塞 I/O 操作时,JVM 自动将其从载体线程上卸载,载体线程可以去执行其他虚拟线程;I/O 完成后再恢复执行

郑天祺大约 10 分钟java基础Java虚拟线程并发编程
interrupt方法对线程的影响

在Java中,interrupt() 方法是 Thread 类中的一个实例方法,用于中断线程。它并不直接终止线程的执行,而是设置线程的中断状态(即把该线程的中断标志设为 true)。线程可以定期检查这个中断状态,以判断是否应该提前退出或改变行为。以下是 interrupt() 方法对线程的影响:

1. 中断状态

  • 当调用 thread.interrupt() 时,如果线程正在运行,那么它的中断状态将被设置为 true。线程可以通过 Thread.currentThread().isInterrupted() 来检查自身的中断状态。
  • 如果线程已经处于中断状态,则再次调用 interrupt() 不会有额外的效果。

郑天祺大约 3 分钟java基础java并发编程线程
JDK并发包常用类

1、工具类

提供并发控制手段: CountDownLatch、CyclicBarrier、Semaphore

线程间数据交换: Exchanger

CountDownLatch:

允许一个或多个线程等待其他线程完成操作。

CountDownLatch的构造函数接受一个int类型的参数作为计数器,你想等待n个点完成,就传入n。

两个重要的方法:

countDown():调用时,n会减1。

await():调用会阻塞当前线程,直到n变成0。

await(long time,TimeUnit unit):等待特定时间后,就不会继续阻塞当前线程。


郑天祺大约 3 分钟java基础java基础并发编程JDK
CountDownLatch

1、CountDownLatch简介

	CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。

	类似的任务可以使用线程的  join()  方法实现:在等待时间点调用其他线程的  join()  方法,当前线程就会等待join线程执行完之后才继续执行,但 CountDownLatch 实现更加简单,并且比 join 的功能更多。

CountDownLatch函数列表

CountDownLatch(int count)
构造一个用给定计数初始化的 CountDownLatch// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void await()
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, TimeUnit unit)
// 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
void countDown()
// 返回当前计数。
long getCount()
// 返回标识此锁存器及其状态的字符串。
String toString()

郑天祺大约 3 分钟java基础并发编程同步工具CountDownLatch
分布式锁

1、什么是分布式锁?

	当多个进程在同一个系统中,用分布式锁控制多个进程对资源的访问。传统的单体应用单机部署情况下,可以使用java并发处理相关的API进行互斥控制。分布式系统后由于多线程,多进程分布在不同机器上,使单机部署情况下的并发控制锁策略失效,为了解决跨JVM互斥机制来控制共享资源的访问,这就是分布式锁的来源;分布式锁应用场景大都是高并发、大流量场景。

2、分布式锁实现

(1)、redis分布式锁的实现

  1. 加锁机制:根据hash节点选择一个客户端执行lua脚本

  2. 锁互斥机制:再来一个客户端执行同样的lua脚本会提示已经存在锁,然后进入循环一直尝试加锁

  3. 可重入机制

  4. watch dog自动延期机制

  5. 释放锁机制


郑天祺大约 19 分钟分布式分布式锁并发编程
并发编程总结

1、Synchronized

	Synchronized是由JVM实现的一种实现互斥同步的一种方式,如果你查看被Synchronized修饰过的程序块编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitor enter和monitor exit两个字节码指令。

	这两个指令是什么意思呢?在虚拟机执行到monitor enter指令时,首先要尝试获取对象的锁︰如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;当执行monitorexit指令时将锁计数器-1﹔当计数器为O时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。

	Java中Synchronize通过在对象头设置标记,达到了获取锁和释放锁的目的。

	Synchronize是非公平锁。

郑天祺大约 5 分钟面试java并发编程面试
AtomicInteger

1、介绍

AtomicInteger属于JUC并发包下的原子类,继承关系如下:

public class AtomicInteger extends Number implements java.io.Serializable

郑天祺大约 6 分钟面试java基础并发编程原子类
ThreadLocal 详解:原理、场景与最佳实践

ThreadLocal 详解:原理、场景与最佳实践

引言

在 Java 并发编程中,经常面临两类问题:

  1. 上下文传递:某个信息(如用户 ID、请求 TraceId)需要在方法调用链中传递,但显式传参会导致接口臃肿
  2. 线程安全:某些工具类(如 SimpleDateFormat)非线程安全,但全局加锁又严重影响性能

ThreadLocal 是 JDK 提供的线程局部变量机制,通过"每个线程持有独立副本"的设计思想,优雅地解决了上述两个问题。


郑天祺大约 10 分钟java基础ThreadLocal并发编程Java
SimpleDateFormat引发的线程安全问题

一、问题产生

在写java程序时,有时间戳转换的操作。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author zhengtianqi
 * @date 2019/10/12
 */
public class DateTrans {

    public static void main(String[] args) {

        // 将2019-10-12 18:50:30 改成 2019年10月12日
        String inDate = "2019-10-12 18:50:30";

        SimpleDateFormat inPut = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat outPut = new SimpleDateFormat("yyyy年MM月dd日");

        try {
            Date temp = inPut.parse(inDate);
            String outDate = outPut.format(temp);

            System.out.println(outDate);

        } catch (ParseException e) {
            System.out.println("时间转换出错,出错信息为 ={}" + e);
        }

    }
}


郑天祺大约 2 分钟java基础线程安全SimpleDateFormat并发编程
池化之线程池

java中池化技术是提前保存大量的资源,以备不时之需以及重复使用。

1、池化技术

在实际应用当做,分配内存、创建进程、线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作。因此,当程序中需要频繁的进行内存申请释放,进程、线程创建销毁等操作时,通常会使用内存池、进程池、线程池技术来提升程序的性能。
进程池、线程池:先启动若干数量的线程,并让这些线程都处于睡眠状态,当需要一个开辟一个线程去做具体的工作时,就会唤醒线程池中的某一个睡眠线程,让它去做具体工作,当工作完成后,线程又处于睡眠状态,而不是将线程销毁。当线程数达到一定数量时,可以在队列中等待。
内存池:内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。


郑天祺大约 13 分钟java基础线程池并发编程池化技术
2