图片.png

悲观锁

锁住同步资源:实现并发控制
假设最坏
悲观锁适合读多写少,悲观并发控制
悲观锁分为共享锁(读锁)和排他锁(写锁) r— —- (根据liunx命令ll)
悲观锁是”先取锁再访问”的保守策略,安全,开销较大,增大死锁概率,降低并行

乐观锁

不借助数据库的锁机制
假设数据不会冲突
只在数据提交时检验,发现冲突返回给用户错误信息,让用户抉择
和悲观锁一样:避免幻读,业务处理时间过长
依据数据本身保证数据的正确性
乐观锁的实现:CAS,版本号控制(数据修改会使version+1)
乐观锁相信不会数据竞争,只在提交时加锁,决不会死锁

AQS

AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。
自旋锁、互斥锁、读锁写锁、条件产量、信号量、栅栏都是AQS的衍生物

AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。

CAS

  • 首先,每个线程都会先获取当前的值,接着走一个原子的 CAS 操作。原子的意思就是这个 CAS 操作一定是自己完整执行完的,不会被别人打断。
  • 然后 CAS 操作里,会比较一下,现在的值是不是刚才获取到的那个值。如果是,说明没人改过这个值,然后设置成累加 1 之后的一个值。
  • 同理,如果有人在执行 CAS 的时候,发现之前获取的值跟当前的值不一样,会导致 CAS 失败。失败之后,进入一个无限循环,再次获取值,接着执行 CAS 操作。

自旋锁

线程在获取锁失败时,一直循环尝试获取锁
线程状态不变,不进入阻塞,减少上下文切换,效率快
自适应自旋锁:自旋时间不再固定,由前一次在同一个锁上的自旋时间以锁的拥有者的状态来决定。

可重入锁

又名递归锁
实现思路:记录当前锁正在被哪个线程使用,采用计数来统计lock和unlock的调用次数。正常情况下,lock和unlock的调用次数应该相等,如果不相等就会死锁。

Lock与synchronized

(前者默认)非公平,悲观,独享,互斥,可重入的重量级锁
synchronized关键字在JVM层实现,监视工具可见,异常自动释放
Lock代码实现,可重写,手动释放(将unLock()放到finally{}中)
竞争不激烈的情况synchronized效率快,~

使用方法:修饰方法,代码块,静态方法,类

synachronized实现原理
https://blog.csdn.net/tongdanping/article/details/79647337
对象在怼内存的结构 (对象头[数组3bit,非数组2bit],实例变量4bit,填充字节)
synachronized锁的是对象头

volatile

被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。
缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期

  • 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某 个变量的值,这新值对其他线程来说是立即可见的。
  • 2)禁止进行指令重排序。

不保证原子性 必须具备以下 2个条件: 1)对变量的写操作不依赖于当前值 2)该变量没有包含在具有其他变量的不变式中 jdk5+后加入了CAS优化

锁粗化/锁消除

锁粗化就是将多次上锁解锁的请求合并为一次同步请求。
锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

偏向锁/轻/重量级锁

  1. 这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

分段锁

一种锁的设计:目的是细化锁的粒度

HashMap线程不安全,扩容的时候可能出现环状列表导致get操作时,cpu空转
导致get操作时,cpu空转;所以在并发的环境下不建议使用HashMap

HashTable线程安全,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁,其他线程只能阻塞
ConcurrentHashMap:每一把锁锁一段数据,这样在多线程访问时不同段的数据时,就不会存在锁竞争,这就是细化锁的粒度

脏读 不可重复读 幻读

脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。 幻读:是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

JUC

juc就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。