synchronized、Lock、volatile和CAS

本文探讨了Java内存模型中的原子性、可见性和有序性,介绍了锁机制、乐观锁与悲观锁的区别,重点讲解了synchronized、Lock和volatile的关键作用。此外,还涵盖了CAS操作和其在JDK中的应用,以及这些概念在实际编程中的应用和总结。

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

 一、Java的内存模型JMM

在多处理器系统中,每个处理器有自己的高速缓存,而他们又共享同一块内存(下文成主存,main memory 主要内存),当多个处理器运算都涉及到同一块内存区域的时候,就有可能发生缓存不一致的现象。

 

二、并发编程的3个基本概念
1.原子性
     定义: 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

    原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。Java中的原子性操作包括:

(1)基本类型的读取和赋值操作,且赋值必须是值赋给变量,变量之间的相互赋值不是原子性操作。

(2)所有引用reference的赋值操作

(3)java.concurrent.Atomic.* 包中所有类的一切操作

2.可见性
   定义:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

   在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。当然,synchronize和Lock都可以保证可见性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

3.有序性
   定义:即程序执行的顺序按照代码的先后顺序执行。

   Java内存模型中的有序性可以总结为:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存主主内存同步延迟”现象。

   在Java内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序不会影响单线程的运行结果,但是对多线程会有影响。Java提供volatile来保证一定的有序性。最著名的例子就是单例模式里面的DCL(双重检查锁)。另外,可以通过synchronized和Lock来保证有序性,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

 

三、锁机制存在的问题

(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。

(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。

volatile是不错的机制,但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。

 

四、悲观锁与乐观锁

独占锁:是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁:就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止(更加有效、乐观锁用到的机制就是CAS,Compare and Swap)。

 

五、synchronized

synchronized是java中的关键词。synchronized 使用 monitor 机制,能提供很稳定的并发性能;

基于悲观锁机制,即线程获得的是独占锁。

 

六、Lock

java.util.concurrent.locks.Lock,是 java 类,使用上更灵活,做到只读并发,读写串行化等,能做一些有意义的功能增强(等待超时,条件唤醒,公平锁...)

本质是基于 cas 操作的乐观锁机制

 

七:volatile

说简单点,volatile就是表示某人或某物是不稳定的、易变的。

volatile作为java中的关键词之一,是变量修饰符,只能用来修饰变量,用以声明变量的值可能随时会别的线程修改。

volatile变量的特征:

1.保证可见性,不保证原子性

  (1)当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去;

  (2)这个写会操作会导致其他线程中的volatile变量缓存无效。

 2.禁止指令重排,保证了有序性

volatile适用场景:

1.适用于对变量的写操作不依赖于当前值,对变量的读取操作不依赖于非volatile变量。

2.适用于读多写少的场景。

3.可用作状态标志。

4.JDK中volatie应用:JDK中ConcurrentHashMap的Entry的value和next被声明为volatile,AtomicLong中的value被声明为volatile。AtomicLong通过CAS原理(也可以理解为乐观锁)保证了原子性。

单例模式,应用volatile:https://www.cnblogs.com/zz-ksw/p/12774426.html

 

八、CAS

CAS,compare and swap的缩写,中文翻译成比较并交换。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

目的:利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。对于synchronized阻塞算法,性能上有了很大的提升。


九、JDK中volatile+CAS(乐观锁)应用

1、JDK中ConcurrentHashMap的Entry的value和next被声明为volatile,AtomicLong中的value被声明为volatile。ConcurrentHashMap、AtomicLong通过CAS原理(也可以理解为乐观锁)保证了原子性。

2、volatile变量的读/写和CAS可以实现线程之间的通信。把这些特性整合在一起,就形成了整个concurrent包得以实现的基石

 

十、总结

Java 语言包含两种内在的同步机制:同步块(或方法)[synchronized、Lock]和 volatile 变量。

volatile具有可见性、有序性,不具备原子性(最终同步还是要回到锁机制上来:应用乐观锁或悲观锁); synchronized、Lock 具备原子性;

volatile不会让线程阻塞,响应速度比synchronized高。

 

https://www.cnblogs.com/dingyingsi/p/3760447.html

https://blog.youkuaiyun.com/u012723673/article/details/80682208

https://blog.youkuaiyun.com/ls5718/article/details/52563959

https://baijiahao.baidu.com/s?id=1595669808533077617&wfr=spider&for=pc

https://naotu.baidu.com/file/bb2b786c56106f64de573db2eda71c3d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值