# 并发 学习笔记 **Repository Path**: maglna1/concurrent-learning-notes ## Basic Information - **Project Name**: 并发 学习笔记 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-05-26 - **Last Updated**: 2022-01-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 并发 学习笔记 #### 介绍 ### 1.悲观锁与乐观锁 **悲观锁** (Pessimistic Lock): **每次获取数据的时候,都会担心数据被修改** ,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。 **乐观锁** (Optimistic Lock): **每次获取数据的时候,都不会担心数据被修改** ,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。 由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。 **悲观锁比较适合强一致性的场景,但效率比较低,特别是读的并发低。** 乐观锁则适用于 **读多写少,并发冲突少** 的场景。 **Synchronized:** Java在 **JDK1.5** 之前都是靠 synchronized关键字保证同步的。这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有共享变量的锁,都采用独占的方式来访问这些变量。这就是一种独占锁, **独占锁** 其实就是一种悲观锁。 **CAS:Locks.ReentrantLock** 、: CAS 操作中包含三个操作数 —— 需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。 在JDK1.5 中新增 **java.util.concurrent (J.U.C)** 就是建立在CAS之上的。相对于对于 synchronized 这种阻塞算法,CAS是非阻塞算法的一种常见实现。 ### 2.CAS与Synchronized的使用情景: 1、对于资源竞争较少 **(线程冲突较轻)** 的情况,使用 **synchronized** 同步锁进行 **线程阻塞和唤醒切换** 以及 **用户态内核态间的切换** 操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。 2、对于资源竞争严重 **(线程冲突严重)** 的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。 补充: synchronized在 **jdk1.6之后,已经改进优化** 。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。 **在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。** ### 3.自旋锁 互斥同步进入阻塞状态的开销都很大,应该尽量避免。在许多应用中,共享数据的锁定状态只会持续很短的一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以 **避免进入阻塞状态** 。 自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。 在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。 参考:[CSDN博客](https://blog.csdn.net/mulinsen77/article/details/89348591)