JVM——线程安全的实现方法

本文深入探讨了Java中并发控制的三种主要策略:互斥同步、非阻塞同步和无同步方案。介绍了synchronized关键字和ReentrantLock的使用,以及它们在实现线程安全方面的区别。此外,还讲解了乐观并发策略的原理及其核心指令CAS,最后讨论了可重入代码和线程本地存储在避免数据争用中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一互斥同步(悲观的并发策略)

同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用。互斥是实现同步的一种手段,下面介绍两种互斥同步的手段:synchronized关键字和concurrent包中的重入锁ReentrantLock

synchronized关键字:

synchronized关键字经过编译之后,会在同步块的前后分别形成monitorenter和monitorexit两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象。如果Java程序中的synchronized明确指定了对象参数,那就是这个对象的reference,如果没有明确执行,根据synchronized修饰的是实例方法还是类方法,去取对应的实例对象或Class对象作为锁对象。

ps:synchronized同步块对同一条线程来说是可重入的,(锁是可以重入的)

同步块在已进入的线程执行完之间,会阻塞后面的其他线程进入。

ReentrantLock:

相比synchronized,ReentrantLock增加了一些高级功能:

  1. 等待可中断是指当持有所的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,可中断性对处理执行时间非常长的同步块很有帮助
  2. 公平锁是指在多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
  3. 锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象,而在synchronized中只能实现一个条件

总结:在synchronized能实现需求的情况下,优先考虑使用synchronized进行同步

二非阻塞同步(乐观的并发策略)

基于冲突检测的乐观的并发策略,先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据由争用,产生了冲突,那就再采取其他的补偿措施

一般靠硬件来实现操作和冲突检测这两个步骤具有原子性,常用的指令:CAS

三无同步方案

可重入代码:这种代码也叫做春代码,可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误。相对于线程安全来说,可重入性是更基本的特性,它可以保证线程安全,即所有的可重入代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。

线程本地存储:如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码能否在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样无需同步也能保证线程之间不出现数据争用的问题。

总结:

Java语言中,如果一个变量要被多个线程访问,可以使用volatile关键字声明它为“易变的”;如果一个变量要被某个线程独享,可以通过java.lang.ThreadLocal类来实现线程本地存储的功能。

 

### JVM多线程环境下的内存抢占原理及解决方案 #### 1. 内存分配的线程安全性保障 在JVM中,当多个线程同时尝试创建对象并将其存储到堆中时,可能会发生内存抢占问题。为了防止这种情况的发生,JVM引入了一种称为 **TLAB (Thread Local Allocation Buffer)** 的机制[^1]。 TLAB 是为每个线程预先分配的一小块连续内存区域,用于该线程单独的对象分配操作。这样可以减少因全局锁而导致的竞争开销,从而提高性能。 如果某个线程的 TLAB 耗尽而无法满足新的对象分配需求,则会触发一次全局同步操作,从共享堆空间中重新分配一个新的 TLAB 或直接分配大对象至堆上。 #### 2. 主内存与工作内存之间的交互 根据 Java 内存模型的规定,主内存主要用于保存程序运行过程中产生的实际数据副本,而每个线程都有自己独立的工作内存[^2]。在线程执行期间,它会先将所需的数据从主内存加载到自己的工作内存中,在此之后所有的读写操作都将在本地完成。只有当必要时才会把修改后的值刷新回主内存[^3]。 这种设计有效地隔离了不同线程间的访问冲突,因为即使两个线程要处理相同的变量,它们实际上是在各自的私有缓存里进行操作而不是直接竞争公共资源。 #### 3. 原子性和可见性保证 对于某些特定的操作如 `volatile` 字段更新或者锁机制内的临界区保护等场景下,还需要额外考虑更细粒度上的同步控制措施以确保原子性和可见性。例如: - 使用 `synchronized` 关键字来显式加锁; - 利用 CAS(Compare And Swap)无锁算法实现高效并发结构(比如 AtomicInteger 类内部就采用了此类技术)。 这些高级特性进一步增强了应用程序面对复杂多线程状况时的安全性和稳定性。 #### 4. 线程模型的选择影响 现代 JVM 实现通常依赖操作系统级别的原生线程支持构建其自身的线程体系架构——即所谓的 “一对一映射关系”,这意味着每一个 Java 用户级线程都会对应到底层 OS 上的一个真实存在的轻量进程实体去执行相应任务[^4]。这样的安排有助于更好地利用硬件资源以及简化跨平台移植难度等问题。 然而值得注意的是,并非所有类型的共享状态都需要如此严格的防护策略。像只读常量表之类的内容天然具备良好的可重入性质,因此完全可以放置于无需特别顾虑竞态条件的方法区内供全体参与者共同享用而不必担心潜在风险存在[^5]。 ```java // 示例代码展示如何通过 synchronized 方法解决可能发生的内存争用情况 public class Counter { private int count; public synchronized void increment() { // 加锁确保每次只有一个线程能进入这个方法体 this.count++; } public synchronized int getCount() { // 同样需要保持一致性视图 return this.count; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值