当前位置: 首页 > news >正文

Java Util Concurrent(JUC)

定义

JUC是Java并发编程工具包(Java Util Concurrent)的缩写,属于java.util.concurrent包及其子包(如java.util.concurrent.atomicjava.util.concurrent.locks)的集合。它提供了高性能、线程安全的并发编程工具,用于简化多线程开发。

创建和运行线程

方法一,直接使用Thread

class MyThread extends Thread { public void run() { System.out.println("Thread is running"); } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }

方法二,实现Runnable接口配合Thread

class MyRunnable implements Runnable { public void run() { System.out.println("Thread is running"); } } public class Main { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); } }

Thread与Runnable的关系

方法一是把线程和任务合并在一起,方法2是把线程和任务分开了

用Runnable更容易与线程池等高级API配合

用Runnable让任务类脱离了Thread继承体系,更灵活

方法三,FutureTask配合Thread

创建Callable任务 定义一个实现Callable接口的任务类,重写call()方法作为线程执行逻辑

创建并启动线程 将Callable任务包装成FutureTask,通过Thread启动

获取执行结果 通过FutureTask的get()方法获取计算结果(会阻塞直到任务完成)

class MyCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); return "Task completed"; } }
FutureTask<String> futureTask = new FutureTask<>(new MyCallable()); Thread thread = new Thread(futureTask); thread.start();
try { String result = futureTask.get(); System.out.println(result); // 输出: Task completed } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }

线程的状态

操作系统层面:初始状态,可运行状态,运行状态,阻塞状态,终止状态。

在Java API层面,根据Thread.State枚举分为六种状态:NEW、RUNNABLE(包含运行状态、可运行状态、阻塞状态)、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。

synchronized优化原理

轻量级锁实现机制:

  • 线程在执行同步块之前,JVM会在当前线程的栈帧中创建锁记录(Lock Record),用于存储对象头的Mark Word副本。
  • 通过CAS操作尝试将对象头的Mark Word更新为指向锁记录的指针,如果成功则获取轻量级锁。
  • 如果CAS操作失败,表示存在竞争,轻量级锁会膨胀为重量级锁。

锁膨胀

如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其他线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。

触发条件

  • 锁数量阈值:单个事务持有的行锁超过数据库设定的上限(如InnoDB默认5000个)。

  • 资源竞争:高并发场景下,系统为减少锁管理开销主动升级锁级别。

  • 死锁风险:系统为避免潜在死锁,提前扩大锁范围。

自旋优化

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。

偏向锁

轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行CAS操作。

Java 6中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word投,之后发现这个线程ID是自己的就表示没有竞争,不用重新CAS。以后只要不发生竞争,这个对象就归该线程所有。

偏向锁

偏向状态

一个对象创建时:如果开启了偏向锁(默认开启),那么对象创建后,markword值为0x05即最后三位为101,这时它的thread、epoch、age都为0

偏向锁是默认有延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数-XX:BiasedLockingStartupDelay=0来禁用延迟。

如果没有开启偏向锁,那么对象创建后,markword值为0x01即最后3位为001,这时它的hashcode、age都为0,第一次用到hashcode时才会赋值。

撤销-调用对象 hashCode

调用了对象的 hashCode,但偏向锁的对象MarkWord中存储的是线程id,如果调用hashCode会导致偏向锁被撤销

轻量级锁会在锁记录中记录hashCode

重量级锁会在Monitor中记录hashCode

在调用hashCode后使用偏向锁,记得去掉-XX:-UseBiasedLocking

撤销-其他线程使用对象

当有其他线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

撤销-调用 wait/notify

批量重偏向

如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程T1的对象仍有机会重新偏向T2,重偏向会重置对象的ThreadID

当撤销偏向锁阈值超过20次后,JVM会这样觉得,我是不是偏向错了,于是会给这些对象加锁时重新偏向至加锁线程。

批量撤销

当撤销偏向锁阈值超过40次后,jvm会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的。

锁消除

逃逸分析是锁消除的基础。JVM通过分析对象的动态作用域,判断对象是否可能被其他线程访问:

  • 如果对象不会逃逸出当前线程(即线程私有),则对应的同步锁可以被消除。

  • 如果对象可能被其他线程访问(即存在竞争条件),则保留同步逻辑。

ReentrantLock(Re entrant Lock )

相对于synchronized具备如下特点:

可中断 可以设置超时时间 可以设置为公平锁 (是一种多线程同步机制,确保线程按照请求锁的顺序(先到先得)获取锁资源。)

支持多个条件变量 与synchronized一样,都支持可重入

public class SyncExample { private int count = 0; public synchronized void increment() { count++; } }
public class LockExample { private final ReentrantLock lock = new ReentrantLock(); private int count = 0; public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } }

可重入性同一个线程可以多次获取同一把锁,而不会导致死锁。每次获取锁后必须对应释放锁,否则会导致锁无法被其他线程获取。

锁中断lockInterruptibly()允许在等待锁时响应中断。

try { lock.lockInterruptibly(); try { // 临界区代码 } finally { lock.unlock(); } } catch (InterruptedException e) { // 处理中断 }

锁超时

尝试获取锁tryLock()方法可非阻塞地尝试获取锁,返回boolean表示是否成功。

ReentrantLock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); }

超时获取锁tryLock(long timeout, TimeUnit unit)支持在指定时间内等待锁。

if (lock.tryLock(1, TimeUnit.SECONDS)) { try { // 锁获取成功 } finally { lock.unlock(); } }

公平锁

ReentrantLock默认是不公平的

通过构造函数指定是否为公平锁:

  • 公平锁:按照线程请求锁的顺序分配锁。
  • 非公平锁:允许插队,可能导致某些线程长时间等待。
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁 ReentrantLock unfairLock = new ReentrantLock(); // 非公平锁(默认)

公平锁一般没有必要,会降低并发度

条件变量(Condition) 通过newCondition()创建条件变量,实现线程间更灵活的协调。

Condition condition = lock.newCondition(); lock.lock(); try { while (!conditionMet) { condition.await(); // 释放锁并等待 } // 条件满足后执行 condition.signal(); // 唤醒一个等待线程 } finally { lock.unlock(); }

synchronized是那些不满足条件的线程都在一间休息室等消息

而ReentrantLock支持多间休息室,有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤醒。

volatile原理

volatile的底层实现原理是内存屏障,Memory Barrier(Memory Fence)

对volatile变量的写指令后会加入写屏障

对volatile变量的读指令前会加入读屏障

可见性保证

volatile 变量的读写操作直接作用于主内存,而非线程的工作内存。当一个线程修改 volatile 变量时,新值会立即刷新到主内存;当其他线程读取该变量时,会直接从主内存中获取最新值,避免因工作内存缓存导致的脏读问题。

底层通过内存屏障(Memory Barrier)实现。写操作后插入 StoreLoad 屏障,强制将写缓冲区的数据刷入主内存;读操作前插入 LoadLoad 屏障,确保读取的是主内存的最新值。

禁止指令重排序

volatile 通过插入内存屏障禁止编译器和处理器对其修饰的变量进行指令重排序。具体规则如下:

  • volatile 写操作前的指令不能重排序到写操作之后;
  • volatile 读操作后的指令不能重排序到读操作之前;
  • volatile 写操作与后续的 volatile 读操作不能重排序。
局限性

volatile 无法保证复合操作的原子性。例如i++操作包含读取、修改、写入三步,volatile 仅保证每次读取的是最新值,但多线程并发时仍可能导致丢失更新。此时需使用synchronizedAtomicInteger

double-checked locking问题

问题根源

在没有正确同步的情况下,Java编译器和处理器可能对指令进行重排序。这会导致其他线程看到未完全初始化的对象实例。具体表现为:

  • 线程A开始初始化对象,但对象的部分构造尚未完成。
  • 线程B通过第一次检查发现实例不为null,直接返回未完全初始化的对象。

错误的实现示例

public class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); // 问题在此 } } } return instance; } }

问题出在instance = new Singleton()这行代码,它实际包含三个步骤:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存地址

由于重排序,步骤2和3可能被颠倒,导致其他线程看到非null但未初始化的实例。

解决方案 使用volatile关键字

从Java 5开始,volatile可以禁止指令重排序,确保对象完全初始化后才被引用:

public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }

自定义线程池

http://www.jsqmd.com/news/449738/

相关文章:

  • 面试题:互斥锁与条件变量,在生产者消费者模型中的使用,lock在条件变量中的作用
  • UE5 编辑器下添加组件
  • 计算机毕业设计springboot校园疫情防范管理系统 高校疫情防控数字化管理平台 基于Spring Boot的校园防疫信息管理系统
  • WebRTC 视频编码丢帧与降低分辨率机制深度剖析
  • 甩锅防御机制:运维说“网络正常”时的专业应对策略
  • IPTV系统解决方案怎么选?从机顶盒到系统平台全解析
  • 计算机毕业设计springboot高校智慧党建管理系统 基于SpringBoot的数字化高校党务工作平台 SpringBoot驱动的大学党建信息化综合服务平台
  • GISBox vs GeoServer:谁才是现代GIS开发的更优解?
  • 大兴机场机位进出方案优化设计研究
  • jQuery day1
  • OpenAI GPT-5.4实测
  • 粉色PCB评测排名:猎板技术可靠,兼具颜值与性能
  • 从“踩坑无数”到“如获至宝”:我如何找到那家真正靠谱的AI服务商?
  • 毕业论文神器!冠绝行业的降AIGC平台 —— 千笔·降AI率助手
  • 内网两台 Linux 服务器高效传输大文件(70GB 实战指南)
  • LEDNum不是二进制数
  • 基于Java与SpringBoot集成卡证检测矫正模型:构建企业级OCR服务
  • 计算机毕业设计springboot室内设计类网站 基于SpringBoot的家居空间数字化设计平台 SpringBoot驱动的室内装潢方案在线定制系统
  • 【2025最新】基于SpringBoot+Vue的springbo共享单车数据存储系统管理系统源码+MyBatis+MySQL
  • 一次多agent情况下openclaw不回消息问题的排查经过(使用飞书通信,持续更新中~~~)
  • 【kv存储】持久化模块优化----内存映射取代拷贝式加载
  • 构建安全桥梁:前后端分离架构下的数据交互与防护指南
  • 基于粒子群算法优化bp神经网络(PSO-BP)回归预测模型 实现平台:Matlab 多特征输入
  • ebmap Tour 导览地图制作之 路网绘制
  • 写作小白救星 10个AI论文平台深度测评,专科生毕业论文写作必备!
  • 实测20款适合东南亚语言配音软件推荐,以下6款全支持
  • 拖延症福音!千笔·专业论文写作工具,领军级的AI论文平台
  • MATLAB高效调试与性能优化全攻略
  • 2026年盘锦大米:揭秘源头厂家背后的秘密与排名!
  • 〘 3-1 〙软考高项 | 第10章:项目进度管理(上)