Java中锁的分类,公平锁、乐观锁、悲观锁、可重入锁

1.锁的分类?

(1)可重入锁、不可重入锁

Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁

重入:当前线程获取到A锁,在获取之后再次尝试获取A锁是可以直接拿的
  
不可重入:当前线程获取到A锁,在获取之后再次尝试获取A锁是无法获取到的,因为A锁被当前线程占用着,需要等待自己释放之后在获取

(2)乐观锁、悲观锁

Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁
Java中提供的CAS操作,就是乐观锁的一种实现

悲观锁:获取不到锁资源的时候,会将当前线程挂起(进入block、waiting),线程挂起会涉及到用户态和内核态的切换,而这种切换是比较消耗资源的
	用户态:JVM	可以自行执行的指令,不需要借助操作系统执行
	内核态:JVM不可以自行执行,需要操作系统才可以执行
	
乐观锁:获取不到锁资源,可以再次让CPU调度,重新尝试获取锁资源 
   Atomic原子类中,就是基于CAS乐观锁实现的

(3)公平锁、非公平锁

Java中提供的synchronized只能是非公平锁
Java中提供的ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁

公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,锁被A线程持有,同时线程B在排队。直接排队到B后面,等待B拿到锁资源或是取消后,才可以尝试去竞争锁资源
非公平锁:线程A获取到锁资源后,线程B没有拿到,线程B去排队,线程C来了,先尝试竞争一波;拿到锁资源,插队成功;没有拿到锁资源,依然要排队到B后面,等待B拿到锁资源或者取消后,才可以尝试去竞争锁资源

(4)互斥锁、共享锁

Java中提供的synchronized,ReentrantLock是互斥锁
Java中提供的ReentrantReadWriteLock,有互斥锁也有共享锁

 互斥锁:同一个时间点,只会有一个线程持有当前互斥锁
 共享锁:同一个时间点,当前共享锁可以被多个线程同时持有

2.synchronized在jdk1.6中的优化?

(1)锁消除:在synchronized修饰的代码中,如果不存在操作临界资源的情况,会触发锁消除,你即便是写了synchronized,也不会触发;是因为jit编译器的优化

(2)锁膨胀:如果一个循环中频繁的获取和释放锁资源,这样带来的消耗很大,锁膨胀就是将锁的范围扩大,避免频繁的竞争和获取锁资源带来不必要的消耗

(3)锁升级:ReentrantLock的实现,是先基于乐观锁的CAS尝试获取锁资源,如果拿不到锁资源,才会挂起线程。synchronized在jdk1.6之前,完全就是获取不到锁,立即挂起当前线程,所以synchronized性能较差

synchronized就在jdk1.6做了锁升级的优化
 -无锁、匿名偏向:当前对象没有作为锁存在
 -偏向锁:如果当前锁资源,只有一个线程在频繁的获取和释放,那么这个线程过来,只需要判断,当前指向的线程是否是当前线程;如果是,直接拿走锁资源;如果不是,基于CAS的方式,尝试将偏向锁指向当前线程,如果获取不到,触发锁升级,升级为轻量级锁(偏向锁出现了锁竞争的情况)
 -轻量级锁:会采用自旋锁的方式去频繁的以CAS的形式获取锁资源(采用的是自适应自旋锁);如果成功获取到,拿走锁资源;如果自旋了一定次数,没有拿到锁资源,锁升级
 -重量级锁:就是传统的synchronized方式,拿不到锁资源,就挂起当前线程(会从用户态转换为内核态)

3.Synchronized的实现原理?

synchronized是基于对象实现的,而对象是存储在jvm的堆中的,对象存储有三部分信息:对象头、实例数据、对象填充;对象头中有MarkWord和ClassPoint,对象的锁信息都是存储在MarkWord中的

4.什么是AQS?

  • AQS就是AbstractQueueSynchronizer抽象类,是JUC包下的一个基类,JUC下的很多内容都是基于AQS实现了部分功能,比如ReentrantLock,ThreadPoolExecutor,阻塞队列,CountDownLatch,Semaphore,CyclicBarrier等等都是基于AQS实现。

  • AQS提供了一个由volatile修饰,并且采用CAS方式修改的int类型的state变量;其次AQS维护了一个双向链表,有head,有tail,并且每个节点都是Node对象

5.AQS唤醒节点时,为何从后往前找?

  • 这种从后往前的遍历方式主要是为了优化唤醒线程的性能。AQS的设计目标之一是减少不必要的上下文切换和线程唤醒操作,以提高并发性能。通过从后往前遍历等待队列,可以更快地找到一个合适的线程进行唤醒

6.ReentrantLock和synchronized的区别?

  • 核心区别:
    • ReentrantLock是个类,synchronized是关键字,当然都是在JVM层面实现的互斥锁 的方式
  • 效率区别:
    • 如果竞争比较激烈,推荐ReentrantLock去实现,不存在锁升级的概念。而synchronized是存在锁升级的概念,如果升级到重量级锁,是不存在锁降级的
  • 底层实现区别:
    • 实现原理不一样,ReentrantLock是基于AQS实现的,synchronized是基于ObjectMonitor
  • 功能方面的区别:
    • ReentrantLock的功能比synchronized更全面,ReentrantLock支持公平锁和非公平锁,ReentrantLock可以指定等待锁资源的时间

7.ReentrantReadWriteLock的实现原理?

  • ReentrantReadWriteLock基于AQS实现的,而且还是对state进行操作的,拿到锁资源就去干活,如果没有拿到,依然会去AQS队列中排队。
    • 读锁操作:基于state的高16位进行操作的。
    • 写锁操作:基于state的低16位进行操作的。
  • ReentrantReadWriteLock是可重入锁,写锁重入是对state进行 +1操作;读锁重入是对state的高16位进行 +1操作,每个读操作的线程会有一个ThreadLocal记录锁重入的次数
    • 写锁饥饿问题是因为读锁绕过写锁,直接拿到锁资源进行操作,导致写锁长时间没有获取到写锁资源
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值